Kombai Logo

7 reasons to choose Kombai over Figma MCP

Figma MCP has recently captured a lot of attention. It’s seen as the current best way to generate code from Figma designs. But is it truly helpful to generate clean, production-ready code? Let’s find out.

In this post, we’ll compare Kombai and Figma MCP based on code quality, asset handling, and fidelity. Then, it’s on you to decide which path makes more sense for you: faster first drafts or production-ready output?

Why is Kombai different?

Best Figma Interpretation Engine

Kombai’s Figma interpretation engine goes beyond simple data fetching. Kombai understands Figma designs like a human, accounting for common "non-ideal" patterns such as incorrect grouping, unintended layers (invisible elements, overlapping nodes), or accidental fills/shadows.

This engine is an evolution of our popular Figma-to-code model, which was Product Hunt's top developer tool of 2023.

Human-Tested RAGs for Frontend Libraries

Using best practices from relevant libraries is crucial for production-ready frontend code. Current agents rely on documentation MCPs (often model-generated extracts). They can be unreliable and fail to account for common pitfalls specific models run into. Some developers try to fix this by writing extensive, agent-specific rules. But maintaining those rules quickly becomes exhausting as models, MCPs, and libraries evolve.

Kombai takes a different approach by using built-in, human-tested context tools for every supported frontend library. These tools give models accurate, version-specific best practices.

Understands Codebase Like a Human

Kombai adopts a more human-like method to understand a codebase. It first identifies the key parameters needed for writing high-quality code within a given repository and then extracts the necessary information, such as code files and configurations.

For example, in a codebase with a custom component library, Kombai tries to understand each reusable component's function, UI appearance, and required props, much like a human developer onboarding to a new codebase.

Comparing Kombai and Figma MCP + Cursor

To compare Kombai with Figma’s Dev Mode MCP, I ran a test. I used the same Figma project in both. One setup used Figma MCP connected to Cursor with Claude Sonnet 4.5. The other used Kombai. Here’s what I found.

Figma design preview

Component Structure

Kombai created a clean, reusable setup. It generated well-defined components like <FilterModal />, <Header />, <OrdersTable />, and <Sidebar />.

It used TanStack’s root route to render the sidebar and header across all pages.

import { createRouter, createRoute, createRootRoute, Outlet } from '@tanstack/react-router';
import { Box, Group } from '@mantine/core';
import Sidebar from './components/Sidebar/Sidebar';
import Header from './components/Header/Header';
import OrdersPage from './pages/OrdersPage';
import OrderDetailsPage from './pages/OrderDetailsPage';
import { mockRootProps } from './data/ordersMockData';

const rootRoute = createRootRoute({
  component: () => (
    <Group gap={0} align="flex-start" wrap="nowrap">
      <Sidebar teamMembers={mockRootProps.teamMembers} />
      <Box style={{ marginLeft: '256px', width: 'calc(100% - 256px)' }}>
        <Header currentTime={mockRootProps.currentTime} userAvatar={mockRootProps.currentUser.avatar} />
        <Outlet />
      </Box>
    </Group>
  ),
});

const indexRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/',
  component: () => <OrdersPage />,
});

const ordersRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/orders',
  component: () => <OrdersPage />,
});

const orderDetailsRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/orders/$orderId',
  component: () => <OrderDetailsPage />,
});

const routeTree = rootRoute.addChildren([indexRoute, ordersRoute, orderDetailsRoute]);

export const router = createRouter({ routeTree });

Cursor made a reasonable layout with <Sidebar />, <Topbar />, <FilterModal />, and <Layout />. But much of the UI remained plain JSX. The Search, Table, and Pagination on the Orders page were not extracted as components. Mantine components were not used.

Mock Data Usage

Kombai created a dedicated file called ordersMockData.ts. This file stores mock data for both the Orders and Order Details pages. It includes details like table rows, customer and payment info, product lists, and shipment summaries.

You can later connect the same UI to a real API by simply replacing the mock file. No UI logic needs to change.

import { Box, Stack, Group, Text, TextInput, Button, Badge, Select, ActionIcon } from '@mantine/core';
import { useState } from 'react';
import OrdersTable from '../components/OrdersTable/OrdersTable';
import FilterModal, { type FilterValues } from '../components/FilterModal/FilterModal';
import SearchIcon from '../components/icons/SearchIcon';
import ChevronDownIcon from '../components/icons/ChevronDownIcon';
import ChevronLeftIcon from '../components/icons/ChevronLeftIcon';
import ChevronRightIcon from '../components/icons/ChevronRightIcon';
import { mockQuery } from '../data/ordersMockData';
import classes from './OrdersPage.module.css';

export default function OrdersPage() {
  const [filterOpened, setFilterOpened] = useState(false);
  const [searchQuery, setSearchQuery] = useState('');
  const [currentPage, setCurrentPage] = useState(1);
  const [itemsPerPage, setItemsPerPage] = useState(14);

  const handleFilterApply = (filters: FilterValues) => {
    console.log('Filters applied:', filters);
    // Implement filter logic here
  };

  const totalItems = 100;
  const totalPages = Math.ceil(totalItems / itemsPerPage);

  return (
    <Box className={classes.container}>
      <Stack gap="lg">
        {/* Page Header */}
        <Group justify="space-between" align="center">
          <Group gap="xs">
            <Badge color="yellow" radius="sm" size="lg" className={classes.pageBadge}>
              {' '}
            </Badge>
            <Text size="32px" fw={700} c="black" lh="40px">Orders</Text>
          </Group>
        </Group>

        {/* Search and Filter */}
        <Group justify="space-between" align="center">
          <TextInput
            placeholder="Search by product #, name, date..."
            leftSection={<SearchIcon width={20} height={20} color="#696f8c" />}
            value={searchQuery}
            onChange={(e) => setSearchQuery(e.currentTarget.value)}
            className={classes.searchInput}
            styles={{
              input: {
                fontSize: '16px',
                color: '#696f8c'
              }
            }}
          />
          
          <Button
            variant="subtle"
            rightSection={<ChevronDownIcon width={8} height={5} color="#0b1234" />}
            onClick={() => setFilterOpened(true)}
            fw={500}
            c="black"
          >
            Filter
          </Button>
        </Group>

        {/* Orders Table */}
        <OrdersTable orders={mockQuery.orders} />

        {/* Pagination */}
        <Group justify="space-between" align="center" mt="md">
          <Group gap="sm">
            <Select
              value={itemsPerPage.toString()}
              onChange={(value) => setItemsPerPage(Number(value))}
              data={['14', '25', '50', '100']}
              w={70}
              styles={{
                input: {
                  fontSize: '14px'
                }
              }}
            />
            <Text size="sm" fw={400} c="black">
              {(currentPage - 1) * itemsPerPage + 1} - {Math.min(currentPage * itemsPerPage, totalItems)} of {totalItems} items
            </Text>
          </Group>

          <Group gap="sm">
            <Select
              value={currentPage.toString()}
              onChange={(value) => setCurrentPage(Number(value))}
              data={Array.from({ length: totalPages }, (_, i) => (i + 1).toString())}
              w={70}
              styles={{
                input: {
                  fontSize: '14px'
                }
              }}
            />
            <Text size="sm" fw={400} c="black">of {totalPages} pages</Text>
            <ActionIcon
              variant="subtle"
              onClick={() => setCurrentPage(Math.max(1, currentPage - 1))}
              disabled={currentPage === 1}
            >
              <ChevronLeftIcon width={6} height={11} color="#0b1234" />
            </ActionIcon>
            <ActionIcon
              variant="subtle"
              onClick={() => setCurrentPage(Math.min(totalPages, currentPage + 1))}
              disabled={currentPage === totalPages}
            >
              <ChevronRightIcon width={6} height={11} color="#0b1234" />
            </ActionIcon>
          </Group>
        </Group>
      </Stack>

      <FilterModal
        opened={filterOpened}
        onClose={() => setFilterOpened(false)}
        onApply={handleFilterApply}
      />
    </Box>
  );
}

Cursor used an inline mock array on the Orders page, relying on initials and background colors instead of avatar URLs. The Order Details page was entirely hardcoded. No mock data source at all. This approach limits scalability. Pages that display transactional information should always rely on a structured mock dataset or an API model.

import { useParams, useNavigate } from '@tanstack/react-router';
import { Layout } from '@/components/Layout/Layout';
import { ArrowLeftIcon, MoreIcon, EditIcon } from '@/components/icons';
import styles from './OrderDetail.module.css';

export const OrderDetailPage = () => {
    const { orderId } = useParams({ strict: false });
    const navigate = useNavigate();

    return (
        <Layout>
            <div className={styles.container}>
                <div className={styles.header}>
                    <button className={styles.backButton} onClick={() => navigate({ to: '/orders' })}>
                        <ArrowLeftIcon />
                    </button>
                    <div className={styles.titleSection}>
                        <div className={styles.tag} />
                        <h1 className={styles.title}>Order #{orderId.padStart(3, '0')}</h1>
                        <span className={styles.badge}>Paid</span>
                        <span className={styles.badgeUnfulfilled}>Unfulfilled</span>
                    </div>
                    <div className={styles.headerActions}>
                        <button className={styles.secondaryButton}>Restock</button>
                        <button className={styles.secondaryButton}>Edit</button>
                        <button className={styles.secondaryButton}>
                            More action
                            <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
                                <path d="M10 12L6 8H14L10 12Z" fill="currentColor" />
                            </svg>
                        </button>
                        <div className={styles.divider} />
                        <button className={styles.iconButton}>
                            <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
                                <path d="M12.5 15L7.5 10L12.5 5" stroke="currentColor" strokeWidth="1.5" />
                            </svg>
                        </button>
                        <button className={styles.iconButton}>
                            <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
                                <path d="M7.5 15L12.5 10L7.5 5" stroke="currentColor" strokeWidth="1.5" />
                            </svg>
                        </button>
                    </div>
                </div>

                <p className={styles.subtitle}>24 Feb 2023 - 15:55 from Draft Orders</p>

                <div className={styles.content}>
                    <div className={styles.leftColumn}>
                        {/* Unfulfilled Section */}
                        <div className={styles.section}>
                            <div className={styles.sectionHeader}>
                                <span className={styles.unfulfilled}>Unfulfilled (1)</span>
                                <button className={styles.moreButton}>
                                    <MoreIcon />
                                </button>
                            </div>
                            <div className={styles.orderItem}>
                                <div className={styles.productImage} />
                                <div className={styles.productInfo}>
                                    <h3>Puffy Fanny Pack</h3>
                                    <p className={styles.variant}>Small Size</p>
                                </div>
                                <div className={styles.priceInfo}>
                                    <span>$45.95</span>
                                    <span>x 1</span>
                                    <span>$45.95</span>
                                </div>
                            </div>
                            <div className={styles.actionButtons}>
                                <button className={styles.fulfillButton}>Fulfill</button>
                                <button className={styles.primaryButton}>Create shipping label</button>
                            </div>
                        </div>

                        {/* Summary Section */}
                        <div className={styles.section}>
                            <div className={styles.sectionHeader}>
                                <div className={styles.sectionTitle}>
                                    <div className={styles.sectionTag} style={{ background: '#b1e5fc' }} />
                                    <h2>Summary</h2>
                                </div>
                                <span className={styles.badge}>Paid</span>
                            </div>
                            <div className={styles.summaryRow}>
                                <div>
                                    <span className={styles.summaryLabel}>Subtotal</span>
                                    <span className={styles.summaryDetail}>1 item</span>
                                </div>
                                <span className={styles.summaryValue}>$45.95</span>
                            </div>
                            <div className={styles.summaryRow}>
                                <div>
                                    <span className={styles.summaryLabel}>Discount:</span>
                                    <span className={styles.summaryDetail}>WeCraft20</span>
                                </div>
                                <span className={styles.summaryValue}>-$9.19</span>
                            </div>
                            <div className={styles.summaryRow}>
                                <div>
                                    <span className={styles.summaryLabel}>Shipping</span>
                                    <span className={styles.summaryDetail}>Free shipping</span>
                                </div>
                                <span className={styles.summaryValue}>$0.00</span>
                            </div>
                            <div className={styles.summaryTotal}>
                                <span>Total</span>
                                <span>$36.76</span>
                            </div>
                            <div className={styles.dividerLine} />
                            <div className={styles.summaryRow}>
                                <span className={styles.summaryDetail}>Paid by customer</span>
                                <span className={styles.summaryValue}>$0.00</span>
                            </div>
                            <div className={styles.summaryRow}>
                                <span className={styles.summaryDetail}>Payment due when invoice is sent</span>
                                <span className={styles.summaryValueBlue}>Edit</span>
                            </div>
                            <div className={styles.invoiceButtons}>
                                <button className={styles.secondaryButton}>Send invoice</button>
                                <button className={styles.primaryButton}>
                                    Collect payment
                                    <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
                                        <path d="M10 12L6 8H14L10 12Z" fill="white" />
                                    </svg>
                                </button>
                            </div>
                        </div>

                        {/* Payments Section */}
                        <div className={styles.section}>
                            <div className={styles.sectionHeader}>
                                <div className={styles.sectionTitle}>
                                    <div className={styles.sectionTag} style={{ background: '#cabdff' }} />
                                    <h2>Payments</h2>
                                </div>
                                <div className={styles.badges}>
                                    <span className={styles.badgeChargeback}>Chargeback</span>
                                    <button className={styles.secondaryButton}>Capture Payment</button>
                                </div>
                            </div>
                            <div className={styles.payment}>
                                <div className={styles.paymentInfo}>
                                    <span className={styles.paymentId}>01TT9SBZGSM5RTX0988W5X5HB</span>
                                    <span className={styles.paymentDate}>24 Feb 2023 - 16:10</span>
                                </div>
                                <div className={styles.paymentAmount}>
                                    <span className={styles.paymentMethod}>Paypal</span>
                                    <span className={styles.paymentValue}>$36.76</span>
                                </div>
                            </div>
                        </div>

                        {/* Customer Section */}
                        <div className={styles.section}>
                            <div className={styles.sectionHeader}>
                                <div className={styles.sectionTitle}>
                                    <div className={styles.sectionTag} style={{ background: '#ff9d8f' }} />
                                    <h2>Customer</h2>
                                </div>
                                <button className={styles.moreButton}>
                                    <MoreIcon />
                                </button>
                            </div>
                            <div className={styles.customerInfo}>
                                <div className={styles.customerAvatar}>L</div>
                                <div>
                                    <h3 className={styles.customerName}>Leslie Alexander</h3>
                                    <p className={styles.customerLocation}>Los Angeles, USA (04:40 AM)</p>
                                </div>
                            </div>
                            <div className={styles.customerDetails}>
                                <div className={styles.customerDetail}>
                                    <span className={styles.detailLabel}>Contact</span>
                                    <div className={styles.detailValue}>
                                        <p>lesliealexander@email.com</p>
                                        <p>(480) 555-0103</p>
                                    </div>
                                </div>
                                <div className={styles.customerDetail}>
                                    <span className={styles.detailLabel}>Phone</span>
                                    <p className={styles.detailValue}>Washington Square S New York, NY 10012, USA</p>
                                </div>
                                <div className={styles.customerDetail}>
                                    <span className={styles.detailLabel}>Billing</span>
                                    <p className={styles.detailValue}>No billing address</p>
                                </div>
                            </div>
                            <div className={styles.customerTags}>
                                <span className={styles.tag}>VIP Customers</span>
                                <span className={styles.tag}>Subscribes</span>
                                <span className={styles.tag}>+3</span>
                            </div>
                        </div>
                    </div>

                    <div className={styles.rightColumn}>
                        {/* Note Section */}
                        <div className={styles.section}>
                            <div className={styles.sectionHeader}>
                                <div className={styles.sectionTitle}>
                                    <div className={styles.sectionTag} style={{ background: '#b1e5fc' }} />
                                    <h2>Note</h2>
                                </div>
                                <button className={styles.iconButton}>
                                    <EditIcon />
                                </button>
                            </div>
                            <p className={styles.noteText}>First customer and order!</p>
                        </div>

                        {/* Timeline Section */}
                        <div className={styles.section}>
                            <div className={styles.sectionHeader}>
                                <div className={styles.sectionTitle}>
                                    <div className={styles.sectionTag} style={{ background: '#ffd88d' }} />
                                    <h2>Timeline</h2>
                                </div>
                                <button className={styles.moreButton}>
                                    <MoreIcon />
                                </button>
                            </div>
                            <div className={styles.commentBox}>
                                <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
                                    <circle cx="7" cy="10" r="1.5" fill="#696F8C" />
                                    <circle cx="10" cy="10" r="1.5" fill="#696F8C" />
                                    <circle cx="13" cy="10" r="1.5" fill="#696F8C" />
                                    <path d="M10 18C14.4183 18 18 14.4183 18 10C18 5.58172 14.4183 2 10 2C5.58172 2 2 5.58172 2 10C2 11.8385 2.63099 13.5279 3.69076 14.8788L2.5 18.5L6.12124 17.3092C7.47208 18.369 9.16147 19 10 19Z" stroke="#696F8C" strokeWidth="1.5" />
                                </svg>
                                <input
                                    type="text"
                                    placeholder="Leave a comment..."
                                    className={styles.commentInput}
                                />
                                <button className={styles.sendButton}>
                                    <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
                                        <path d="M18 2L9 11M18 2L12 18L9 11M18 2L2 8L9 11" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
                                    </svg>
                                </button>
                            </div>

                            <div className={styles.timeline}>
                                <div className={styles.timelineItem}>
                                    <div className={styles.timelineIcon}>📧</div>
                                    <div className={styles.timelineContent}>
                                        <div className={styles.timelineHeader}>
                                            <span className={styles.timelineEmail}>hi.wecrafts@gmail.com</span>
                                            <button className={styles.moreButton}>
                                                <MoreIcon />
                                            </button>
                                        </div>
                                        <p className={styles.timelineDate}>24 Feb 2023 - 15:55</p>
                                        <p className={styles.timelineMessage}>Return will be shipped together with return 74421</p>
                                    </div>
                                </div>

                                <div className={styles.timelineItem}>
                                    <div className={styles.timelineIcon}>📦</div>
                                    <div className={styles.timelineContent}>
                                        <div className={styles.timelineHeader}>
                                            <span className={styles.timelineTitle}>Shipment Notice Sent</span>
                                            <button className={styles.moreButton}>
                                                <MoreIcon />
                                            </button>
                                        </div>
                                        <p className={styles.timelineDate}>
                                            24 Feb 2023 - 15:55
                                            <span className={styles.timelineArrow}>→</span>
                                            lesliealexander@emai...
                                        </p>
                                    </div>
                                </div>

                                <div className={styles.timelineItem}>
                                    <div className={styles.timelineIcon}>✓</div>
                                    <div className={styles.timelineContent}>
                                        <div className={styles.timelineHeader}>
                                            <span className={styles.timelineTitle}>Order placed</span>
                                            <span className={styles.timelineAmount}>$36.76</span>
                                        </div>
                                        <p className={styles.timelineDate}>
                                            24 Feb 2023 - 15:55
                                            <span style={{ marginLeft: '80px' }}>VISA *5623</span>
                                        </p>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </Layout>
    );
};

Tech Stack Integrity

Kombai installed all packages: React 19, TypeScript, Mantine v8, and TanStack Router. Mantine components were used throughout the UI. And, the routing implementation was clean and correct for routes.

Cursor installed the requested stack. While TanStack Router caused some issues, it was later implemented for routing (after I followed up). Mantine components were rarely used for the UI. Except for the <FilterModal /> component, the rest of the project relies on plain JSX.

Interactivity

Kombai is trained to infer how UI elements should behave. In the generated code, dropdowns such as More Actions and Collect Payment are fully functional.

Kombai used Mantine’s <DatePickerInput /> component. When clicked, the input opened a fully functional calendar popover. Users can select a date instead of typing text. This reflects Kombai’s understanding of UX.

Cursor implemented only the explicitly defined behaviors from the prompt. The Filter opened correctly, and table rows navigated to the details page. Dropdowns like More Actions and Collect Payment were static. And, the date field was a simple text input rather than an interactive date picker.

Setup Errors

Kombai set up and ran the project smoothly, with no errors related to any library. Kombai’s built-in, human-tested RAGs allow it to apply framework-specific best practices. Because of this, setup issues are rare when working with Kombai.

Cursor, however, ran into a few setup errors. It initially used Tailwind’s justify-center utility, where justify-content: center; was expected. It was a mistake from Cursor, since generic LLMs tend to default to Tailwind syntax. It also faced a TanStack Router error- Duplicate routes found with id: root. In total, it took me three prompts to resolve these problems.

TanStack Router Error

Asset Handling

Kombai extracted over 35 unique images and icons from Figma and rendered them accurately, closely matching the original design. It did, however, miss one product image on the Order Details page and a customer avatar in the Orders table. Considering the large number of assets processed, this was understandable.

Assets Kombai fetched

I easily added these two missing assets to the mockData file. Since Kombai already integrated most icons and images precisely, adding one or two missing items was a quick fix.

Cursor failed to fetch several key assets. This included the site logo, customer avatars, and the product image. After follow-up prompts, it substituted them with pravatar.cc and Unsplash URLs.

export const mockOrders: Order[] = [
  { 
    id: '#001', 
    customer: { name: 'Bessie Cooper', avatarBg: '#062753', initials: 'B' }, 
    purchase: 'Baby Shark Bundle', 
    date: '24 Feb - 15:55', 
    price: '$475.22', 
    status: 'Paid' 
  },
  { 
    id: '#002', 
    customer: { name: 'Jerome Bell', avatarUrl: 'https://i.pravatar.cc/24?img=12' }, 
    purchase: 'Fortifying Moisturizer', 
    date: '24 Feb - 15:55', 
    price: '$328.85', 
    status: 'Paid' 
  },
  { 
    id: '#003', 
    customer: { name: 'Jane Cooper', avatarBg: '#48052a', initials: 'J' }, 
    purchase: 'The Blissful Blend Coffee', 
    date: '24 Feb - 15:55', 
    price: '$767.50', 
    status: 'Chargeback' 
  },
  { 
    id: '#004', 
    customer: { name: 'Cameron Williamson', avatarUrl: 'https://i.pravatar.cc/24?img=13' }, 
    purchase: 'Puffy Fanny Pack', 
    date: '24 Feb - 15:55', 
    price: '$473.85', 
    status: 'Paid' 
  },
  { 
    id: '#005', 
    customer: { name: 'Courtney Henry', avatarUrl: 'https://i.pravatar.cc/24?img=5' }, 
    purchase: 'Standard Baggu + 3 more', 
    date: '24 Feb - 15:55', 
    price: '$779.58', 
    status: 'Paid' 
  },
  { 
    id: '#006', 
    customer: { name: 'Leslie Alexander', avatarUrl: 'https://i.pravatar.cc/24?img=47' }, 
    purchase: 'Plender', 
    date: '24 Feb - 15:55', 
    price: '$169.43', 
    status: 'Chargeback' 
  },
  { 
    id: '#007', 
    customer: { name: 'Darrell Steward', avatarBg: '#471e08', initials: 'D' }, 
    purchase: 'Weekender Mens Coba.. + 2 more', 
    date: '24 Feb - 15:55', 
    price: '$406.27', 
    status: 'Paid' 
  },
  { 
    id: '#008', 
    customer: { name: 'Jenny Wilson', avatarUrl: 'https://i.pravatar.cc/24?img=20' }, 
    purchase: 'Baby Stroller Caddy Cotton', 
    date: '24 Feb - 15:55', 
    price: '$782.01', 
    status: 'Returned' 
  },
  { 
    id: '#009', 
    customer: { name: 'Floyd Miles', avatarBg: '#1e1348', initials: 'F' }, 
    purchase: 'Fortifying Moisturizer', 
    date: '24 Feb - 15:55', 
    price: '$601.13', 
    status: 'Swap' 
  },
  { 
    id: '#010', 
    customer: { name: 'Darlene Robertson', avatarBg: '#4c1008', initials: 'D' }, 
    purchase: 'Rose Hoodie', 
    date: '24 Feb - 15:55', 
    price: '$351.02', 
    status: 'Paid' 
  },
];

For icons, it managed to extract 12 unique ones from the two Figma designs.

Icons cursor used in code

However, several icon components did not match the originals. For example, the Home and Calendar icons looked noticeably different from those in Figma.

Calender icons SVG preview

This happened because Figma MCP’s Remote server only supports the get_screenshot tool. That means Cursor can’t actually pull images or icons from the Figma file. It can only take screenshots of whole frames.

You could get around this by using the Desktop server. But it only lets you connect to one Figma file at a time. Since my use case involved multiple Figma files, I had to stick with the Remote server.

Fidelity

Kombai’s design fidelity was largely accurate. Initially, it missed the Status column and the More Actions button column, and showed a few minor styling inconsistencies.

After two follow-ups and a few small adjustments, the result looked like this:

It was not perfect yet, but the output was significantly improved. It’s easy to refine, and much closer to the original Figma design.

Cursor’s initial output had several fidelity issues- incorrect images, mismatched icons, and inconsistent styling. I followed up twice in Cursor to ensure it pulled the correct assets from Figma, added mock data, and used the Mantine component library.

Even after these efforts, the final result was far from the original Figma design:

Kombai Works Well on Existing Codebases Too!

Kombai isn’t just for starting new projects. It fits perfectly into existing codebases too. When your repo already has reusable components, Kombai automatically picks them up and uses them for new designs. You don’t have to manually map your Figma components to your codebase components like you would with Code Connect. You don’t even need to have Figma components for your designs. Kombai will reuse components from your repo, regardless.

To test this, I expanded the same project I used for the earlier comparison. I split it into smaller subcomponents to make the structure cleaner, then added a new page to see how Kombai would perform in an ongoing codebase. Here’s the Figma preview of the new Products page I added:

Figma design preview

The fidelity was excellent. Kombai followed all the same code hygiene principles discussed earlier- consistent component structure, clear mock data separation, accurate asset handling, and proper use of the stack.

Kombai also reused existing components from the repo wherever it made sense. For example, in the new Products page, it automatically used the existing <PageHeader />, <SearchBar />, and <Pagination /> components.

If you’d like to see Kombai perform on a much larger, highly active codebase, check out the Roles & Permissions page we built in the Sygnoz repo.

Conclusion

Kombai is the best way to turn Figma designs into clean code, far better than any MCP. It doesn’t just copy the layout. It understands how your design should work. It knows modern frontend libraries and learns your codebase as it writes.

As a result, the code it generates fits naturally into your repo.