Figma to React: Component Architecture for SaaS Products
The Architecture Decision That Determines Everything Else
When you receive a Figma file for a SaaS product and start planning the React implementation, the first decision you make — how to structure your component tree — cascades through every subsequent choice. Get the architecture right and adding features feels natural, theming is straightforward, and new team members understand the codebase quickly. Get it wrong and you accumulate duplicated components, inconsistent behaviour, and a codebase that fights you on every change.
After building component architectures for dozens of SaaS products, we have settled on patterns that consistently produce maintainable, extensible codebases. This article covers those patterns in detail — not abstract theory, but the specific architectural decisions we make when translating a Figma design system into production React components.
Atomic Design Is a Starting Point, Not a Religion
Brad Frost's atomic design methodology — atoms, molecules, organisms, templates, pages — provides useful vocabulary for thinking about component hierarchy. But applying it rigidly to SaaS products creates problems. A SaaS data table is not an atom, a molecule, or an organism — it is a complex interactive system with its own internal hierarchy of sub-components that does not map neatly to any atomic category.
We use atomic thinking as a heuristic, not a taxonomy. The useful principle is dependency direction: simpler components should not depend on more complex ones. A Button should not import a Modal. A TextInput should not know about the Form it sits inside. Beyond that, we name and organise components based on what they do in the product context, not which atomic category they belong to.
In practice, our component directory structure for a typical SaaS product looks like this: primitives (buttons, inputs, typography, icons), composites (form groups, card layouts, navigation items), features (data tables, chart panels, metric displays), and layouts (page shells, dashboard grids, sidebar arrangements). Each layer can import from layers below it but never from layers above.
Compound Components Over Configuration Props
One of the most common architecture mistakes in SaaS frontends is building components that accept dozens of configuration props instead of using composition. Consider a dashboard card component. The configuration approach would create a single DashboardCard component with props for title, subtitle, icon, headerAction, bodyContent, footerAction, loading state, error state, and collapse behaviour. This produces a component with an unwieldy API that covers common cases but fights you on every edge case.
The compound component approach creates a DashboardCard that renders a container, then provides DashboardCard.Header, DashboardCard.Body, and DashboardCard.Footer as composable sub-components. Each sub-component handles its own concerns. Need a custom header layout for one specific card? Compose it differently. Need a card without a footer? Simply omit it. The compound pattern maps naturally to how designers think in Figma — where components are composed from slot-based instances, not configured through property panels.
This pattern becomes especially powerful with React Context. The parent DashboardCard component provides shared state (collapsed, loading, error) through context, and child sub-components consume it. This means DashboardCard.Body can automatically show a skeleton when the parent is loading, without any explicit prop passing.
Provider Patterns for Theming
SaaS products frequently need multiple visual themes — at minimum, light and dark mode. Many also need white-label theming for enterprise clients, or brand-specific variations for different product tiers. The theme system architecture needs to support all of these without requiring component-level changes.
Our approach uses a ThemeProvider at the application root that maps Figma design tokens to CSS custom properties. Components never reference colour values, spacing values, or typography values directly — they reference CSS custom properties that resolve differently depending on the active theme. This means a theme switch is a single CSS variable update, not a cascade of component re-renders.
For SaaS products that need white-label support, we extend this pattern with a tenant configuration layer. Each tenant can override specific tokens — primary colours, logo, font family — while the base design system remains intact. Components do not need to know which tenant is active because they reference the same CSS custom properties regardless.
State Management Strategy for Dashboard Components
SaaS dashboard components often have complex internal state — a table with sort state, filter state, pagination state, and selection state, all of which may need to persist across navigation or synchronise with URL parameters. The temptation is to reach for a global state manager immediately, but this creates tight coupling between components that should be independent.
Our pattern is local-first state management. Each dashboard component manages its own state internally using React hooks. When state needs to persist (pagination position across navigation) or synchronise (filter selections reflected in the URL), we use custom hooks that bridge local state to the persistence layer. The component itself does not know or care whether its state lives in memory, in the URL, or in localStorage — it interacts with the same hook API regardless.
For inter-component communication — where filtering in one panel should update a chart in another — we use a lightweight event bus or React Context rather than pushing everything through global state. This keeps components independently testable while still enabling the coordinated behaviour that dashboards require.
TypeScript Contracts Between Figma and Code
Every Figma component variant becomes a TypeScript type. This is not just a code quality preference — it is an architectural contract. When the design team adds a new button variant in Figma, the corresponding TypeScript type must be updated, which triggers a type error everywhere the component is used, which forces every usage to explicitly handle the new variant. The type system enforces design system integrity at compile time.
We define prop types that mirror Figma's variant structure exactly. If a Figma component has variants for size, style, and state, the React component has typed props for size, style, and state with exactly the same options. Adding a new variant in Figma means adding a new union member in TypeScript. This 1:1 mapping means the design team and the engineering team are always working from the same source of truth, expressed in different tools.
Folder Structure That Scales
For SaaS products that start with 20 components and grow to 200, the folder structure matters more than most teams realise. We co-locate component files — the component, its styles, its tests, its Storybook stories, and its types all live in the same directory. This means deleting a component is a single folder deletion, not a scavenger hunt across multiple directories.
Each component directory exports through an index file, and component directories are grouped by their architectural layer. A barrel file at each layer provides clean imports. The result is an import like from '@/components/features/DataTable' rather than from '../../../components/DataTable/DataTable'. As the component library grows from tens to hundreds of components, this structure remains navigable because the layered grouping provides natural organisation.
If you are planning a SaaS product build from Figma designs and want an architecture that scales, book a free consultation. We will review your designs and recommend the component architecture that fits your product's specific needs.

Custom SaaS Development
Web App Development
API Development