How the Brand Kit Extracts Design Systems from Figma in Agent-Native
The brand kit extracts design systems from Figma by uploading the raw .fig file to a server endpoint, streaming it to Builder's remote service, and returning job identifiers that let users monitor the asynchronous indexing process.
The BuilderIO/agent-native repository provides a seamless bridge between Figma files and reusable design systems. When you import a Figma file into the brand kit, the system validates the upload, streams the binary data to Builder's backend, and generates a centralized design system containing colors, fonts, and spacing tokens. This architecture avoids local parsing of the proprietary Figma format while ensuring design tokens remain centrally accessible.
Uploading the Figma File from the Client
The extraction process begins in the Design System Setup page, where users select a .fig file through a file picker interface.
The File Selection Handler
In templates/design/app/pages/DesignSystemSetup.tsx, the handleFigImport function constructs a FormData payload and POSTs it to the /api/import-figma-system endpoint:
const handleFigImport = useCallback(
async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file?.name.toLowerCase().endsWith('.fig')) return;
const body = new FormData();
body.append('file', file);
const res = await fetch(appApiPath('/api/import-figma-system'), {
method: 'POST',
body,
});
const json = await res.json();
if (!res.ok) throw new Error(json?.error ?? 'Upload failed');
setFigResult(json); // ← stores Builder result
},
[],
);
This client-side handler performs basic extension validation before delegating the heavy lifting to the server.
Server-Side Validation and Forwarding
The API route processes the multipart request and prepares the data for Builder's indexing service.
Parsing the Multipart Request
In templates/design/server/handlers/import-figma-system.ts, the importFigmaSystem handler parses the request body, enforces a 200 MiB size limit, and sanitizes the filename into a suggested title:
export const importFigmaSystem = defineEventHandler(async (event) => {
const parts = await readMultipartFormData(event);
const part = parts?.find(p => (p.name === 'file' || p.name === 'fig') && p.data);
if (!part) return { error: 'No .fig file uploaded' };
// Validate size (max 200 MiB)
if (part.data.length > MAX_FIG_BYTES) {
setResponseStatus(event, 413);
return { error: `File too large (max ${MAX_FIG_BYTES / 1024 / 1024} MB).` };
}
const suggestedTitle = (part.filename || 'Imported brand')
.replace(/\.fig$/i, '')
.replace(/[-_]+/g, ' ')
.trim();
return await startBuilderDesignSystemIndex({
projectName: suggestedTitle,
files: [{ name: part.filename || 'brand.fig', data: part.data, mimeType: 'application/octet-stream' }],
});
});
The MAX_FIG_BYTES constant ensures uploads do not exceed the service capacity before forwarding to the core indexing logic.
Streaming to Builder's Remote Service
The actual extraction occurs entirely within Builder's backend infrastructure. The agent-native code acts as a secure conduit, handling credentials and upload orchestration.
The Core Indexing Function
In packages/core/src/server/builder-design-systems.ts, the startBuilderDesignSystemIndex function manages the three-phase upload process:
export async function startBuilderDesignSystemIndex(
options: BuilderDesignSystemIndexOptions,
): Promise<BuilderDesignSystemIndexResult> {
const credentials = await resolveBuilderDesignSystemCredentials();
// 1️⃣ Create upload session → get resumable URLs
const uploadStart = await fetchWithTimeout(
makeBuilderDesignSystemUrl('upload/start', credentials),
{ method: 'POST', headers: makeBuilderHeaders(credentials), body: JSON.stringify({ attachments: ... }) }
);
const slots = (await uploadStart.json()).uploads ?? [];
// 2️⃣ Upload each file
for (let i = 0; i < slots.length; i++) {
await uploadToResumableUrl(slots[i], files[i]);
}
// 3️⃣ Trigger the indexing job
const generate = await fetchWithTimeout(
makeBuilderDesignSystemUrl('generate', credentials),
{ method: 'POST', headers: makeBuilderHeaders(credentials), body: JSON.stringify({ uploads: uploadTokens, projectName: options.projectName }) }
);
const generated = await generate.json();
return {
ok: true,
source: 'builder',
projectId: generated.projectId!,
jobId: generated.jobId!,
designSystemId: generated.designSystemId!,
suggestedTitle: options.projectName?.trim() || null,
builderUrl: builderDesignSystemUrl(generated.designSystemId!),
status: 'in-progress',
};
}
Credential Resolution and Upload Flow
The function first resolves Builder credentials using BUILDER_PRIVATE_KEY and BUILDER_PUBLIC_KEY environment variables. It then creates a resumable upload session with Google Cloud Storage, streams the raw .fig binary to the provided URL, and finally POSTs a generate request to start the asynchronous indexing job. The returned BuilderDesignSystemIndexResult contains the unique identifiers required to track the job.
Displaying Indexing Results and Job Status
Once the server returns the result, the front-end stores it in the figResult state. The FigImportPreview component renders the job identifiers and provides a direct link to the Builder design system:
- projectId: The Builder project containing the indexed system
- designSystemId: The unique identifier for the brand kit reference
- builderUrl: A direct URL to view and manage the extracted tokens
- status: Always returns
"in-progress"initially, indicating the asynchronous processing has begun
Users can reference this design system by its designSystemId for subsequent operations like asset generation or design system indexing.
Summary
- File upload: Users select
.figfiles inDesignSystemSetup.tsx, which POSTs to/api/import-figma-systemusinghandleFigImport. - Server validation: The
importFigmaSystemhandler enforces a 200 MiB limit and sanitizes filenames before forwarding. - Builder integration:
startBuilderDesignSystemIndexinbuilder-design-systems.tshandles credential resolution, resumable uploads, and job creation. - Asynchronous processing: The design system extraction happens remotely on Builder's backend, not locally.
- Result tracking: The system returns
projectId,jobId, anddesignSystemIdfor monitoring viaFigImportPreview.
Frequently Asked Questions
What file format does the brand kit support for Figma imports?
The brand kit exclusively supports .fig files—the native Figma binary format. The client-side validation in DesignSystemSetup.tsx explicitly checks for this extension before initiating the upload.
Is there a file size limit when uploading Figma files?
Yes. The server handler enforces a maximum file size of 200 MiB (approximately 200 MB). Files exceeding this limit return a 413 error status with an appropriate message before any data reaches Builder's remote service.
Where does the actual design system extraction happen?
The extraction occurs entirely on Builder's backend service. The agent-native code only handles credential resolution, file streaming, and job initiation. It does not parse the proprietary Figma binary format locally; instead, it delegates to Builder's design-system indexing infrastructure.
How can I check the status of an imported design system?
The startBuilderDesignSystemIndex function returns a BuilderDesignSystemIndexResult containing a builderUrl field. You can navigate to this URL to view the indexing status and access the extracted design tokens. The FigImportPreview component automatically renders this link in the UI immediately after upload.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →