I've lost count of how many times a designer has handed me a beautiful Figma file and said, "It should be pretty straightforward to build, right?" And sure, the hero section looks simple enough. But then you start digging into responsive behavior, hover states that weren't fully specced, spacing that shifts between frames, and a design system that exists in the designer's head but nowhere in code. The gap between a Figma mockup and a production Next.js website is where projects go sideways.

After building dozens of Figma-to-Next.js projects at Social Animal, I've developed strong opinions about what works and what doesn't. This guide walks through the entire process — not the theoretical version, but the messy, real-world version where designs aren't perfect, stakeholders change their minds, and you need to ship something that actually performs well in production.

Figma to Next.js: The Complete Guide to Turning Designs Into Code

Table of Contents

Why Next.js for Figma-to-Code Projects

You could turn Figma designs into plain HTML. You could use Astro, Remix, or SvelteKit. So why Next.js?

A few reasons that matter in practice:

  • React component model maps directly to Figma components. Designers think in components. React thinks in components. This alignment isn't trivial — it means your component tree in code can mirror the component hierarchy in Figma, which makes maintenance far easier.
  • App Router with Server Components gives you the rendering flexibility that marketing sites and web apps both need. Static pages? Server-rendered dynamic content? Client-side interactivity? You pick per-route.
  • Image optimization is built in. The next/image component handles responsive images, lazy loading, and format conversion — things that would otherwise eat hours of your build time.
  • The ecosystem is massive. Whatever the design calls for — auth, forms, animations, CMS integration — there's a well-maintained solution in the Next.js ecosystem.

We use Next.js for the majority of our headless CMS development projects for exactly these reasons. If you're curious about when Astro might be a better fit (hint: content-heavy sites with minimal interactivity), check out our Astro development page.

Auditing the Figma File Before Writing Any Code

This is the step most developers skip, and it's the step that saves the most time. Before you write a single line of JSX, spend 30-60 minutes auditing the Figma file.

What to Check

  • Auto Layout usage. If the designer used Auto Layout consistently, your life gets dramatically easier. Auto Layout maps almost 1:1 to flexbox. If they didn't, you'll be guessing at spacing and responsive behavior.
  • Component consistency. Are buttons actually using a shared component, or did the designer create 14 slightly different button variants across frames? Open the Assets panel and check.
  • Named styles and variables. Figma Variables (released in 2023, widely adopted by 2025) should define colors, spacing, typography, and border radii. If these exist, your design token extraction is mostly automated. If they don't, flag it before you start building.
  • Responsive frames. Does the design include mobile, tablet, and desktop breakpoints? If it's desktop-only, you need a conversation with the designer before proceeding.
  • Missing states. Hover, focus, active, disabled, loading, error, empty — check if interactive components have all their states designed. They usually don't. Make a list.

The Handoff Conversation

I always schedule a 30-minute call with the designer before starting implementation. We screen-share the Figma file and walk through:

  1. Which components are reusable vs. one-off
  2. How responsive behavior should work (don't assume — ask)
  3. Any animations or transitions they have in mind
  4. Content that will come from a CMS vs. hardcoded

This single meeting eliminates 80% of the back-and-forth that typically plagues design-to-code projects.

Figma to Next.js: The Complete Guide to Turning Designs Into Code - architecture

Extracting Design Tokens From Figma

Design tokens are the bridge between Figma and code. Colors, typography scales, spacing units, border radii, shadows — these need to be extracted systematically, not eyeballed.

Manual Extraction (Small Projects)

For smaller projects, I'll use Figma's Dev Mode (included in Figma's paid plans at $25/seat/month as of 2025) to inspect values directly. Open Dev Mode, click on any element, and you get exact pixel values, colors, and font properties.

Then I map these to Tailwind CSS config or CSS custom properties:

// tailwind.config.ts
import type { Config } from 'tailwindcss'

const config: Config = {
  theme: {
    extend: {
      colors: {
        brand: {
          50: '#f0f4ff',
          100: '#dbe4ff',
          500: '#4c6ef5',
          600: '#3b5bdb',
          700: '#364fc7',
          900: '#1c2d7a',
        },
        surface: {
          primary: '#ffffff',
          secondary: '#f8f9fa',
          tertiary: '#f1f3f5',
        },
      },
      fontFamily: {
        sans: ['Inter', 'system-ui', 'sans-serif'],
        display: ['Cal Sans', 'Inter', 'system-ui', 'sans-serif'],
      },
      spacing: {
        '18': '4.5rem',
        '88': '22rem',
      },
      borderRadius: {
        'xl': '1rem',
        '2xl': '1.5rem',
      },
    },
  },
}

export default config

Automated Extraction (Larger Projects)

For bigger design systems, use the Figma Variables API or tools like Tokens Studio (formerly Figma Tokens) to export design tokens in a structured format. Tokens Studio can export to Style Dictionary format, which you then transform into Tailwind config, CSS variables, or both.

The pipeline looks like this:

Figma Variables → Tokens Studio → Style Dictionary → tailwind.config.ts + globals.css

This automation pays for itself the first time the designer updates a color and you need to propagate it across the codebase.

Setting Up Your Next.js Project Architecture

Here's the project structure I start with for every Figma-to-Next.js build:

src/
├── app/
│   ├── layout.tsx
│   ├── page.tsx
│   ├── globals.css
│   └── (routes)/
├── components/
│   ├── ui/           # Primitives: Button, Input, Card, Badge
│   ├── layout/       # Header, Footer, Container, Section
│   ├── sections/     # Hero, Features, Testimonials, CTA
│   └── patterns/     # Composed: PricingCard, TeamMember
├── lib/
│   ├── utils.ts
│   └── fonts.ts
├── styles/
│   └── tokens.css    # Design token CSS variables
└── types/
    └── index.ts

Key Setup Decisions

Styling approach: Tailwind CSS is my default for Figma-to-code projects. The utility-first approach means I can translate Figma's padding: 24px, gap: 16px, border-radius: 12px directly to p-6 gap-4 rounded-xl without context-switching. If the project requires a component library like shadcn/ui, Tailwind is already the foundation.

Font loading: Always use next/font to self-host fonts. Here's my typical setup:

// lib/fonts.ts
import { Inter } from 'next/font/google'
import localFont from 'next/font/local'

export const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-inter',
})

export const calSans = localFont({
  src: '../assets/fonts/CalSans-SemiBold.woff2',
  variable: '--font-display',
  display: 'swap',
})

Server vs. Client Components: Default to Server Components. Only add 'use client' when you actually need browser APIs, event handlers, or React hooks. A typical marketing page might have 90% Server Components with small interactive islands.

Building the Component Library

This is where the bulk of the work happens. My approach: work from the smallest components up.

Atomic Components First

Start with what Figma calls "components" and what we call primitives:

// components/ui/Button.tsx
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'

const buttonVariants = cva(
  'inline-flex items-center justify-center rounded-xl font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
  {
    variants: {
      variant: {
        primary: 'bg-brand-600 text-white hover:bg-brand-700',
        secondary: 'bg-surface-secondary text-gray-900 hover:bg-surface-tertiary',
        ghost: 'text-gray-600 hover:bg-surface-secondary hover:text-gray-900',
      },
      size: {
        sm: 'h-9 px-3 text-sm',
        md: 'h-11 px-5 text-sm',
        lg: 'h-13 px-7 text-base',
      },
    },
    defaultVariants: {
      variant: 'primary',
      size: 'md',
    },
  }
)

interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {}

export function Button({ className, variant, size, ...props }: ButtonProps) {
  return (
    <button
      className={cn(buttonVariants({ variant, size }), className)}
      {...props}
    />
  )
}

Notice how the variant names and sizes map directly to what exists in Figma. If the designer has a Button component with "Primary", "Secondary", and "Ghost" variants in Figma — your code should mirror those exact names.

Composing Sections

Once primitives are built, compose them into page sections:

// components/sections/Hero.tsx
import { Button } from '@/components/ui/Button'
import { Container } from '@/components/layout/Container'

export function Hero() {
  return (
    <section className="py-24 md:py-32">
      <Container>
        <div className="mx-auto max-w-3xl text-center">
          <h1 className="font-display text-4xl tracking-tight text-gray-900 md:text-6xl">
            Turn your designs into
            <span className="text-brand-600"> production websites</span>
          </h1>
          <p className="mt-6 text-lg leading-relaxed text-gray-600">
            We build fast, accessible Next.js websites from your Figma files.
          </p>
          <div className="mt-10 flex items-center justify-center gap-4">
            <Button size="lg">Get started</Button>
            <Button variant="secondary" size="lg">Learn more</Button>
          </div>
        </div>
      </Container>
    </section>
  )
}

AI-Assisted Figma to Code Tools in 2025

Let's talk about the elephant in the room: AI tools that claim to convert Figma to code automatically. I've tested all the major ones. Here's an honest assessment.

Tool Best For Code Quality Framework Support Price (2025)
Fusion (Builder.io) Teams using Builder.io's CMS Good — respects design systems React, Next.js, Vue Included with Builder.io plans ($50+/mo)
Kombai VS Code users wanting AI-assisted coding Very good — generates editable plans React, Next.js, Angular Free tier + $20/mo Pro
Locofy.ai Quick prototypes and MVPs Decent — needs cleanup React, Next.js, Gatsby Free tier + $8-25/mo
Anima Responsive HTML/React export Fair — structural but not production-ready React, Vue, HTML Free tier + $39/mo
Figma to Code Plugin Quick HTML snippets Basic — good starting point HTML, Tailwind Free
v0 (Vercel) Generating UI from descriptions Good for components React, Next.js Free tier + $20/mo Pro

My Honest Take

None of these tools produce code I'd ship directly to production without significant modification. Not one. Here's why:

  • They generate markup but rarely understand your project's component architecture
  • They don't know about your data fetching patterns, CMS integration, or API structure
  • They often produce bloated CSS or inconsistent class naming
  • They miss accessibility requirements regularly

Where AI tools genuinely help: I use Kombai and v0 to generate initial component scaffolding, especially for complex layouts. Getting a starting point that's 60-70% correct saves real time. I also use Cursor with Figma screenshots pasted as context to speed up section-by-section implementation.

The workflow that actually works: AI generates a rough draft → human developer restructures, optimizes, and integrates → QA catches the inevitable issues.

If you're evaluating whether to DIY this or work with an agency, check our Next.js development capabilities to see how we handle the full pipeline.

Handling Responsive Design the Right Way

Here's where Figma-to-code projects commonly fall apart. The design has a desktop mockup and a mobile mockup. Maybe a tablet one if you're lucky. But the actual behavior between breakpoints? That's in nobody's head.

The Mobile-First Implementation

Always code mobile-first and add complexity at larger breakpoints:

<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3 lg:gap-8">
  {features.map((feature) => (
    <FeatureCard key={feature.id} {...feature} />
  ))}
</div>

Common Responsive Patterns From Figma

Figma Pattern CSS/Tailwind Implementation
3-column grid → stacks on mobile grid grid-cols-1 md:grid-cols-3
Side-by-side → reversed stack flex flex-col-reverse md:flex-row
Hidden on mobile hidden md:block
Different font sizes text-2xl md:text-4xl lg:text-5xl
Horizontal scroll on mobile flex overflow-x-auto md:grid md:grid-cols-4
Navigation → hamburger Client component with state toggle

Container Queries (The Underused Power Move)

In 2025, container queries have excellent browser support (95%+ globally). They're perfect for components that need to adapt based on their parent's width rather than the viewport:

@container (min-width: 400px) {
  .card-layout {
    flex-direction: row;
  }
}

Tailwind v4 has native container query support with @container variants.

Typography and Spacing: Where Most Projects Fail

I'd estimate 60% of "it doesn't look like the design" complaints come down to typography and spacing, not layout or colors.

Typography Checklist

  • Font weight: Figma shows "Semi Bold" which is font-semibold (600), not font-bold (700). Easy to get wrong.
  • Line height: Figma uses fixed line heights (like 28px), Tailwind uses relative values (like leading-7). Convert carefully.
  • Letter spacing: Often overlooked. Figma's -2% letter spacing translates to tracking-tight.
  • Font features: Some designs use OpenType features like tabular numbers (font-variant-numeric: tabular-nums) or stylistic alternates. Check the Figma text properties panel.

Spacing System

If the designer used an 8px grid (most do in 2025), your life is easy — Tailwind's default spacing scale is already based on 4px increments. p-4 = 16px, p-6 = 24px, p-8 = 32px.

But watch for irregular spacing. If the design has 20px padding somewhere, that's p-5 in Tailwind (which is 20px). If it has 18px — and this happens more than you'd think — you either round to the nearest step or extend your spacing scale.

Images, Icons, and Assets Pipeline

Images

Always use next/image for raster images:

import Image from 'next/image'

<Image
  src="/hero-image.webp"
  alt="Product dashboard showing analytics"
  width={1200}
  height={800}
  priority  // Add for above-the-fold images
  className="rounded-2xl"
/>

Export images from Figma at 2x resolution for retina displays. Use WebP format. For hero images, I typically export at 2400x1600 and let next/image handle responsive sizing.

Icons

Don't export icons as images. Use an icon library or inline SVGs:

  1. Lucide React — my default choice. Clean, consistent, 1000+ icons. Tree-shakeable.
  2. Heroicons — great if the design uses Heroicons (common with Tailwind UI designs).
  3. Custom SVGs — for brand-specific icons, export from Figma as SVG and create React components.
import { ArrowRight, Check, X } from 'lucide-react'

<ArrowRight className="h-5 w-5" />

Animations and Interactions

Figma's prototype mode shows transitions and interactions, but translating these to code requires interpretation.

CSS-First Animations

For simple hover effects and transitions, stick with CSS:

<button className="transform transition-all duration-200 hover:scale-105 hover:shadow-lg">
  Get Started
</button>

Framer Motion for Complex Animations

For scroll-triggered animations, page transitions, or complex sequences:

'use client'

import { motion } from 'framer-motion'

export function FadeInSection({ children }: { children: React.ReactNode }) {
  return (
    <motion.div
      initial={{ opacity: 0, y: 20 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true, margin: '-100px' }}
      transition={{ duration: 0.5, ease: 'easeOut' }}
    >
      {children}
    </motion.div>
  )
}

Remember: this has to be a Client Component. Keep the animation wrapper thin and pass Server Components as children when possible.

Connecting to a Headless CMS

Most marketing sites built from Figma designs need a CMS for at least some content. This is where headless CMS development becomes critical.

The pattern I use most often with Next.js App Router:

// app/blog/[slug]/page.tsx
import { getPostBySlug } from '@/lib/cms'
import { notFound } from 'next/navigation'

export async function generateStaticParams() {
  const posts = await getAllPosts()
  return posts.map((post) => ({ slug: post.slug }))
}

export default async function BlogPost({ params }: { params: { slug: string } }) {
  const post = await getPostBySlug(params.slug)
  if (!post) notFound()

  return (
    <article className="prose prose-lg mx-auto max-w-3xl">
      <h1>{post.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.content }} />
    </article>
  )
}

This is a Server Component by default — no 'use client' needed. The CMS data is fetched at build time (with ISR for updates), giving you fast page loads and fresh content.

Quality Assurance and Design Comparison

Here's my QA checklist for every Figma-to-Next.js project:

  1. Visual overlay comparison — Use a tool like PixelSnap or the browser extension "PerfectPixel" to overlay the Figma export on top of your built page. I aim for a 95%+ match, not pixel-perfect. Absolute pixel perfection across all browsers and screen sizes is a myth.

  2. Lighthouse audit — Target 90+ on all four scores. For our projects, we typically hit 95+ on Performance, 100 on Accessibility, 100 on Best Practices, and 100 on SEO.

  3. Cross-browser testing — Chrome, Firefox, Safari (especially Safari — it's always Safari). Test on actual iOS devices, not just Chrome DevTools mobile simulation.

  4. Keyboard navigation — Tab through every interactive element. Focus rings should be visible and logical.

  5. Content stress testing — What happens when a headline is 3x longer than the placeholder? When an image is a different aspect ratio? Real CMS content will break designs that only worked with perfect lorem ipsum.

Performance Optimization

A beautiful design that scores 40 on Lighthouse is a failure. Here's what I do on every project:

  • Lazy load below-fold images (Next.js does this by default)
  • Preload critical fonts with next/font
  • Minimize Client Components — every 'use client' boundary adds JavaScript
  • Use dynamic imports for heavy components: const Chart = dynamic(() => import('./Chart'), { ssr: false })
  • Optimize third-party scripts with next/script and strategy="lazyOnload"

A well-built Next.js site from Figma designs should score 90+ on Lighthouse without heroic optimization efforts. If you're scoring lower, you've probably got too many Client Components or unoptimized images.

If you're looking for help with a Figma-to-Next.js project and want these kinds of results, take a look at our pricing or reach out directly.

FAQ

How long does it take to convert a Figma design to a Next.js website? It depends heavily on the project's complexity. A 5-page marketing site with a clean design system typically takes 2-4 weeks for a skilled developer. A complex web application with dozens of unique components, custom animations, and CMS integration can take 6-12 weeks. The Figma file quality matters a lot — well-organized files with consistent components can cut development time by 30-50%.

Can AI tools fully automate Figma to Next.js conversion? Not yet. As of mid-2025, tools like Builder.io's Fusion, Kombai, and Locofy.ai can generate useful starting points, but none produce production-ready code without significant human intervention. They're best used as accelerators — generating the initial 60-70% of markup — while a developer handles architecture, optimization, accessibility, and CMS integration.

Should I use Tailwind CSS or CSS Modules for Figma-to-code projects? Tailwind CSS is the better fit for most Figma-to-code projects. Figma designs are expressed as concrete values (colors, pixel spacing, font sizes), and Tailwind's utility classes map directly to those values. CSS Modules work fine but add an abstraction layer that slows down the translation process. The one exception: if your team already has a mature CSS Modules codebase, maintaining consistency may outweigh the translation speed benefits.

What's the best way to handle Figma design tokens in Next.js? Use Figma Variables (or Tokens Studio plugin) to export tokens in a structured format, then transform them into your styling system's configuration. For Tailwind, this means extending tailwind.config.ts. For CSS custom properties, generate a tokens.css file. The Style Dictionary tool by Amazon is excellent for transforming tokens between formats. Keep the pipeline automated so design token changes propagate to code without manual work.

How do I handle responsive design when the Figma file only has desktop mockups? This is common. First, talk to the designer and establish responsive behavior expectations. Then implement mobile-first, making layout decisions based on your understanding of the design intent. Use CSS Grid and Flexbox to create naturally responsive layouts. Where you're unsure, stub it out and get designer feedback on the live build — it's much faster to iterate on a real responsive implementation than to go back and design more static frames.

Do I need Figma's paid plan to do proper Figma-to-code development? The free plan works for basic inspection, but Figma's Dev Mode (available on paid plans at $25/seat/month in 2025) provides significantly better development handoff features: CSS code snippets, component property inspection, precise measurements, and asset export. For professional projects, it's worth the cost. Your alternative is using the free Figma to Code plugin or an external tool like Locofy.ai.

What Lighthouse score should I target for a Figma-to-Next.js build? Aim for 90+ across all categories (Performance, Accessibility, Best Practices, SEO). Next.js gives you a strong starting point, but you can easily tank your Performance score with unoptimized images, too many Client Components, or heavy third-party scripts. For our projects at Social Animal, we typically achieve 95+ on Performance by keeping Client Component boundaries minimal and using next/image for all raster graphics.

How do I keep the Figma design and Next.js codebase in sync over time? This is the ongoing challenge. Use design tokens as the single source of truth — when colors, typography, or spacing change in Figma, update the tokens and regenerate your Tailwind config. For component-level changes, establish a process: designers update the Figma component, document what changed, and developers update the corresponding React component. Tools like Storybook can help by providing a visual reference that both designers and developers can check against the Figma source.