figmascope's default export target is Jetpack Compose. But the bundle is agent-agnostic — the IR node kinds, token format, and string refs map just as cleanly to React + Tailwind. You just tell the agent to target JSX instead of Kotlin.

This guide covers the IR-to-JSX mapping, how to extend tailwind.config.js with tokens from tokens.json, and the prompting patterns that produce the least drift on the React side.

One important caveat upfront

Compose is what figmascope tests most. The IR, token format, and CONTEXT.md constraints were designed with Compose in mind. React + Tailwind works — the IR maps cleanly — but you'll see slightly more drift, especially around typography and overlay layouts. Flag anything that looks off and run a token check after the first pass.

Step 1: Generate and unzip the bundle

unzip ~/Downloads/context-bundle.zip -d ./design/

ls design/
# CONTEXT.md  _meta.json  components/  screens/  strings.json  tokens.json

Step 2: Extend Tailwind config with design tokens

Before prompting, map tokens.json into your tailwind.config.js. This is the key step that makes Tailwind token-aware — instead of hardcoded hex values in className strings, you get semantic names that trace back to the design.

// tailwind.config.js
const tokens = require('./design/tokens.json')

// Build color map: { 'brand-7f5cfe': '#7F5CFE', ... }
const colors = Object.fromEntries(
  Object.entries(tokens.color).map(([key, val]) => [
    `brand-${key}`, val
  ])
)

// Build spacing map: { 'ds-4': '4px', 'ds-8': '8px', ... }
const spacing = Object.fromEntries(
  Object.entries(tokens.spacing).map(([key, val]) => [
    `ds-${key}`, `${val}px`
  ])
)

// Build border-radius map
const borderRadius = Object.fromEntries(
  Object.entries(tokens.radius).map(([key, val]) => [
    `ds-${key}`, val === 9999 ? '9999px' : `${val}px`
  ])
)

module.exports = {
  content: ['./src/**/*.{js,ts,jsx,tsx}'],
  theme: {
    extend: {
      colors,
      spacing,
      borderRadius,
    }
  }
}

This produces Tailwind classes like bg-brand-7f5cfe, p-ds-16, rounded-ds-12. Not the prettiest class names, but they're traceable — every class maps back to a token key.

If you'd rather have semantic aliases, add an aliasing layer:

// in theme.extend.colors:
primary: tokens.color['7f5cfe'],
background: tokens.color['f6f2ea'],
surface: tokens.color['ffffff'],

Give the agent both — the raw token config and the semantic aliases — so it can pick the right name in context.

Step 3: Prompt the agent to target React + Tailwind

CONTEXT.md says the default target is Compose. Override it explicitly in your prompt:

Implement ./design/screens/home.json as a React functional component using Tailwind CSS.

Framework override: ignore the Compose instructions in CONTEXT.md. Target React + Tailwind.

Rules:
- Read ./design/tokens.json. The tokens are extended into Tailwind config —
  use Tailwind classes that correspond to token keys (e.g. bg-brand-7f5cfe, p-ds-16).
- UI strings come from ./design/strings.json. Import and reference them by key.
- IR node kind mapping:
    stack (axis:vertical)   → <div className="flex flex-col gap-[Xpx]">
    stack (axis:horizontal) → <div className="flex flex-row gap-[Xpx]">
    overlay                 → <div className="relative"> with absolute-positioned children
    absolute                → <div className="absolute" style={{top, left, width, height}}>
    leaf (text)             → <span> or <p> with Tailwind text classes
    leaf (rectangle)        → <div> with bg and rounded classes
- Do not hardcode hex values. All colors must use Tailwind classes from the token config.
- Do not hardcode strings. Use the strings.json keys.

Output to: src/screens/HomeScreen.tsx

The IR-to-JSX mapping

Here's the full mapping table for React + Tailwind:

IR kindPropertiesJSX pattern
stackaxis: "vertical"<div className="flex flex-col gap-ds-{n}">
stackaxis: "horizontal"<div className="flex flex-row gap-ds-{n}">
overlaylayered children<div className="relative"> with className="absolute ..." children
absolutex, y, w, h<div className="absolute" style={{"{{"}}top: y, left: x, width: w, height: h{{"}}"}}>
leaftype: "text"<span className="text-{size} font-{weight} leading-{lh} text-brand-{color}">
leaftype: "rectangle"<div className="bg-brand-{color} rounded-ds-{r}">

Gap and padding values: use the ds-{n} spacing classes from the Tailwind config extension. If the gap value isn't a clean token key, use Tailwind's arbitrary value syntax: gap-[20px].

A real example: home screen IR to JSX

Using the same IR from the Compose walkthrough:

import strings from '../../design/strings.json'

export function HomeScreen() {
  return (
    <div className="flex flex-col gap-ds-24 p-ds-16 bg-brand-f6f2ea min-h-screen">
      <h1 className="text-2xl font-bold leading-tight text-brand-1a1a2e">
        {strings['home.title'].value}
      </h1>
      <div className="flex flex-col gap-ds-12">
        <div className="bg-white rounded-ds-12 p-ds-16">
          <span className="text-xs font-medium text-brand-7f5cfe">
            {strings['home.card.label'].value}
          </span>
        </div>
      </div>
    </div>
  )
}

Every className is traceable. gap-ds-24spacing.24 → 24px. text-brand-7f5cfecolor.7f5cfe#7F5CFE. If a value drifts (e.g., the agent writes gap-6 instead of gap-ds-24), it's visible immediately in a code review.

Why tokens.json + Tailwind config works

Both systems are token-first. Tailwind extends a base config with custom values; tokens.json is already structured as a custom value map. The extension step (Step 2 above) is a one-time setup — after that, the agent uses Tailwind class names that are semantically tied to the design system rather than arbitrary utility classes.

The result: when the design token changes (say, primary color shifts from #7F5CFE to #6B4EE6), you update one value in tokens.json, re-run the config import, and Tailwind regenerates. The component code doesn't change.

Token drift check

After implementation, ask the agent to audit for drift:

Audit src/screens/HomeScreen.tsx for token drift.

Check:
1. Any hex color values not referenced through a Tailwind class from the token config.
2. Any hardcoded px or rem spacing values that should be ds-{n} classes.
3. Any UI strings not sourced from design/strings.json.

List violations. Produce a diff that fixes them.

Overlay and absolute — the tricky cases

Overlay nodes need a relative parent and absolute-positioned children. The IR lists children in z-order (first = bottom layer). Tell the agent to preserve this order in the JSX — React renders in DOM order and CSS position: absolute stacks accordingly.

Absolute nodes use raw pixel coordinates from Figma. These almost always come from designs that weren't built with auto-layout. If you're seeing a lot of absolute nodes, it usually means the Figma file has manual positioning — the generated code will be brittle at different viewport sizes. Consider flagging this and refactoring to flex layouts.

String handling in React

Unlike Android (where strings.json keys map to R.string.* resource IDs), in React you import the JSON directly:

import strings from '../../design/strings.json'

// usage
{strings['home.title'].value}
// or with fallback
{strings['home.title']?.value ?? strings['home.title']?.fallback ?? 'Untitled'}

If you're using i18n libraries (react-i18next, next-intl), the dot-notation keys in strings.json map directly to translation key namespaces. Tell the agent which i18n library you're using so it generates the right call pattern.

Honest gaps on the React side

Typography utilities. Tailwind's text utilities (text-xs, text-2xl) don't map 1:1 to the typography tokens in tokens.json. Tailwind has a fixed type scale; the token file has arbitrary sizes. You'll need to either extend Tailwind's fontSize config with token values or use arbitrary values (text-[24px]). Both work; extending the config is cleaner.

Line height. Same issue — Tailwind's leading utilities don't match arbitrary lineHeight values. Use leading-[{value}] or extend the config.

Gradients. Not supported in the IR. Any gradient fill appears as a warning in _meta.json and is omitted from the node's fill property. Handle manually.

None of these are blockers — they're known gaps. The token-aware foundation is solid; the edges just need manual handling.

Start with figmascope to export your bundle, then use this guide alongside the Cursor workflow or Claude Code workflow to drive implementation.