Livestream: Building landing pages with AI | 4/3

Announcing Visual Copilot - Figma to production in half the time

Builder logo
builder.io
Contact SalesGo to App

Livestream: Building landing pages with AI | 4/3

Announcing Visual Copilot - Figma to production in half the time

Builder logo
builder.io

Blog

Home

Resources

Blog

Forum

Github

Login

Signup

×

Visual CMS

Drag-and-drop visual editor and headless CMS for any tech stack

Theme Studio for Shopify

Build and optimize your Shopify-hosted storefront, no coding required

Resources

Blog

Get StartedLogin

Component mappings in Angular involve creating .mapper.ts files that connect Figma components to your Angular code components. These mappings define how Figma component properties translate to Angular component properties.

A component mapping file links a specific Figma component to an Angular component in your codebase. To identify the Figma component, you can use either:

  • componentKey: a unique identifier for the Figma component
  • url: the Figma URL that points to the component (more human-readable)

Both methods achieve the same result, but the URL approach is often more convenient for manual creation.

These mapping files are just normal files in your repository that can be created manually. However, for convenience, the CLI also provides a command to generate them more easily with AI assistance.

Once this files exist in your project, use npx builder.io@latest figma publish to publish them to your Builder Space.

Angular component mappings use HTML template literals, html\`…`, instead of the standard Angular template syntax. This section explains the reasoning behind this approach and how to use it effectively.

Builder uses HTML template literals for component mappings for several reasons:

  1. Framework agnostic: this syntax provides a universal way to describe templating across HTML-based frameworks like Angular, Vue, or Web Components
  2. Lightweight: it doesn't require large external dependencies that would bloat your application
  3. Well-tested API: the syntax is battle-tested and widely used in libraries like lit-element.

Note that the template in a mapper function is not an actual Angular component. These mappers are declarative definitions that run outside the Angular runtime, serving as a bridge between Figma components and your Angular components.

The table below compares standard Angular template syntax to HTML template literal syntax:

FeatureStandard Angular templateHTML template literal

Binding properties

[property]="value"

property=${value}

Binding boolean attributes

[disabled]="isDisabled"

disabled=${isDisabled}

String attributes

attribute="value"

attribute="value"

Event binding

(click)="handleClick()"

@click=${handleClick}

Class binding

[class.active]="isActive"

class="active ${isActive ? 'enabled' : ''}"

Style binding

[style.color]="color"

style="color: ${color};"

Interpolation

{{ value }}

${value}

Template reference

#templateRef

id="some-id" (and reference by DOM ID)

Component selector

<app-component>

<app-component> (same)

Component reference

n/a

$cmp=${Component}

Here are a few examples to help you understand how to use HTML template literals in your component mappings:

import { MyButton } from "@/components/ui/button";
// Standard Angular template
// <button-component [variant]="primary" [size]="large"></button-component>

// HTML template literal in mapper
figmaMapping({
  componentKey: "button-component-key",
  mapper(figma) {
    return html`
      <button-component
        color=${figma.Color?.toLowerCase()}
        size=${figma.Size?.toLowerCase()}
        type=${figma.Variant?.toLowerCase()}
        $cmp=${MyButton}
      >
        ${figma.$children}
      </button-component>
    `;
  },
});
import { MuiButton } from "@angular/material";

figmaMapping({
  componentKey: "material-button-key",
  mapper(figma) {
    return html`
      <button
        mui
        variant=${figma.Variant}
        color=${figma.Color}
        $cmp=${MuiButton}
      >
        ${figma.Label}
      </button>
    `;
  },
});
import { ConditionalComponent } from "@/components/conditional";

figmaMapping({
  componentKey: "conditional-component-key",
  mapper(figma) {
    return html`
      ${figma.ShowElement
        ? html`<div visible=${figma.Visible} $cmp=${ConditionalComponent}>
            Content
          </div>`
        : ""}
    `;
  },
});
import { ButtonComponent } from "@/components/button";

// Standard Angular template
// <button (click)="handleClick()">Click me</button>

// HTML template literal in mapper - Note: event handlers are rarely used in mappers
// as they're typically defined in your actual components
figmaMapping({
  componentKey: "button-component-key",
  mapper(figma) {
    return html`
      <button-component
        click=${() => console.log("clicked")}
        disabled=${figma.Disabled}
        $cmp=${ButtonComponent}
      >
        Click me
      </button-component>
    `;
  },
});
import { ListComponent } from "@/components/list";

// Standard Angular template
// <ul><li *ngFor="let item of items">{{item}}</li></ul>

// HTML template literal in mapper
figmaMapping({
  componentKey: "list-component-key",
  mapper(figma) {
    const items = figma.Items || [];
    return html`
      <ul $cmp=${ListComponent} items=${items}>
        ${items.map((item) => html`<li>${item}</li>`)}
      </ul>
    `;
  },
});
  1. Property Binding: Use property=${value} for property binding (Angular's [propName]).
  2. Boolean Attributes: Use disabled=${condition} syntax for boolean attributes that should be present or absent based on a condition.
  3. Expression Interpolation: Use ${expression} anywhere inside the template to insert dynamic values.
  4. Conditional Rendering: There's no direct equivalent to *ngIf - use ternary expressions: ${condition ? html : ''}.
  5. List Rendering: There's no direct equivalent to *ngFor - use JavaScript's map() to transform arrays into templates.
  6. Template Structure: The template must have a single root element or fragment. If you need multiple root-level elements, wrap them in a container.
  7. Component Selector Names: Angular components typically use kebab-case selectors in templates, so continue using that convention in your HTML template literals.
  8. Component Reference: Always use $cmp=${Component} to reference the actual component implementation.

Note that these mappers are not creating actual Angular components — they're just defining how Figma components map to your existing Angular components. The HTML template literal syntax is a way to express this mapping in a framework-agnostic way.

The CLI provides a more convenient approach with AI assistance to help you bootstrap mappings more quickly. Notice that both approaches (manual and CLI) lead to the same result, creating [componentName].mapper.ts files in your project.

The CLI-based workflow leverages AI to automatically generate appropriate mappings between your Figma designs and code components. It analyzes the Figma component, your code components, and any additional documentation to figure out the best mapper function.

1. Use the Builder Figma Plugin: Open the Builder Figma Plugin and navigate to the Design System tab. The plugin scans your selection and identifies unmapped components.

2. Select Components in Figma: Select the components you want to map to your code components.

3. Generate the CLI Command: In the Design System tab of the plugin, there's a list of unmapped components along with a CLI command. This command includes the Figma IDs for all unmapped components. Copy command from the plugin.

# Example command
npx builder.io@latest figma generate --token [TOKEN]

The long string in the command is the Figma component ID, which uniquely identifies the component in Figma. This ID is automatically extracted by the plugin and inserted into the command.

4. Run the Command: Open your terminal in your project directory and paste the command. When you run it, the CLI:

  • Looks up the Figma components based on their IDs
  • Scans your codebase for potential matching components

5. Select Local Components: The CLI displays a list of code components found in your project and prompt you to select which ones should be mapped to your Figma components. Use the arrow keys to navigate and press Enter to select a component.

Select a code component to map ❖ PrimaryButton
❯ ★ Button from '@/components/ui/button'
  Card from '@/components/ui/card'
  ⏭️  Skip (Ctrl+C)
  🏗️  Scaffold Mapper (Generate interface only)
  📦 External npm package

The CLI suggests the best matches first (marked with a star, ★), based on name similarity. If your component is in an external package, select that option to choose from installed packages.

6. Add Documentation (Optional): The CLI will ask if you want to provide a documentation URL for your component. For optimal results, provide a URL to documentation that includes actual code examples showing how to use the component:

Providing a docs URL for Button can drastically improve results: (Ctrl+C to skip)
> https://material.angular.io/components/button/overview

Adding a documentation URL significantly improves the quality of generated mappings, as the AI can learn from the component's official API documentation. For best results, provide URLs to pages that contain actual code examples of how to use the Angular component, showing properties, event handlers, and common patterns.

Documentation with practical code samples is much more valuable than pages with only theoretical explanations or API references without examples.

7. Review AI-Generated Mappings: The CLI uses AI to suggest appropriate mappings between your Figma and code components. It will show you the suggested mapping code:

import { figmaMapping, html } from "@builder.io/dev-tools/figma";
import { Button } from "@/components/ui/button";

figmaMapping({
  componentKey: "9ca66d3a1f5b2c4e7d8a0b9f",
  mapper(figma) {
    return html`
      <button-component
        variant=${figma.Variant?.toLowerCase()}
        size=${figma.Size?.toLowerCase()}
        disabled=${figma.State === "Disabled"}
      >
        ${figma.$textContent || figma.Text || "Button"}
      </button-component>
    `;
  },
});

8. Refine the Mapping: If needed, you can provide natural language feedback to improve the mapping:

How does the mapping look? Reply "good", or provide feedback (Ctrl+C to exit)
> The button should use figma.Label for the text content instead of $textContent

The AI updates the mapping based on your feedback and show you the revised code. You can continue this feedback loop until you're satisfied with the mapping.

You can give specific instructions like "Map the 'Color' property to the 'color' attribute" or "Use the first child as the icon."

9. Save the Mapping: Once you're satisfied with the mapping, the CLI will ask where to save it:

Where do you want to save the new mapping? (Ctrl+C to exit)
> src/mappings/Button.mapper.ts

The default location is usually src/mappings/[ComponentName].mapper.ts, but you can specify any valid path within your project.

10. Publish the Mapping: After creating one or more mappings, publish them to your Builder Space:

npx builder.io figma publish

This displays:

Searching for mapping files...
Found 3 mapping files

Validating mappings...

You are about to publish 3 mapping files to your Builder.io space.
Do you want to continue? (Y/n)
> Y

Publishing mappings to Builder.io...
✓ Published 3 mapping files successfully

Your mappings are now available in the Builder Figma plugin

This section outlines possible issues with the process along with their remedies.

Component Not Found: If the CLI can't find your component, it will offer to scaffold a basic mapping

No matching components found for 'CustomButton'.
Do you want to scaffold a basic mapping? (Y/n)
> Y

Authentication Problems: Refresh your authentication with

npx builder.io figma auth --force

Mappings Not Working in Figma: Verify that the CLI and Figma plugin are using the same Builder.io space, and try republishing with the verbose flag:

npx builder.io figma publish --verbose

TypeScript Errors During Publishing: If the publish command fails with TypeScript errors or validation issues, you can bypass these checks using the --force flag:

npx builder.io figma publish --force

In this case, the CLI returns a warning but does proceed with publishing:

TypeScript errors in src/mappings/Button.mapper.ts:
 • Cannot find name 'ButtonProps'
 • Property 'variant' does not exist on type '{}'

Local mappings contain some errors, but --force flag was used, skipping.

This is particularly useful when:

  • You're dealing with complex TypeScript types the CLI doesn't understand.
  • The errors are in parts of the code that won't affect the mapping functionality.
  • You need to publish quickly and plan to fix the issues later.

Caution: while --force means you can publish despite errors, it's best practice to fix the underlying issues when possible, as they might cause problems when using the Figma plugin.

Begin by learning how to create mappings manually to maintain full control over how Figma designs translate to code.

1. Create a Mapper File: Create a file with the naming convention [componentName].mapper.ts in your project. Many developers use a dedicated mappings directory to organize these files, for example: src/mappings/Button.mapper.ts.

2. Import Required Dependencies:

import { figmaMapping, html } from "@builder.io/dev-tools/figma";
import { ButtonComponent } from "@/path/to/your/component";

3. Define Your Mapping Function: This is where you write the logic that transforms Figma properties into Angular component properties. You need to identify your Figma component using the url parameter:

// Using the URL approach (more human-readable)
figmaMapping({
  url: "https://www.figma.com/file/abc123/Design-System?node-id=456:789",
  mapper(figma) {
    return html`
      <button-component
        .property1=${figma.Property1}
        .property2=${figma.Property2}
      >
        ${figma.$children}
      </button-component>
    `;
  },
});

The URL approach is often preferred for manual mapping because it's more human-readable, easier to obtain directly from Figma, and provides a direct link back to the visual component.

4. Test Your Mapping: Run your local development server with Devtools enabled to test your mapping.

5. Publish Your Mapping: Use the CLI command to publish your manually created mapping:

npx builder.io figma publish
  1. Open your Figma design file.
  2. Select the component you want to map.
  3. Right-click on the selection.
  4. Choose Copy Link To Selection from the context menu.
  5. Paste the URL into your mapping file.

While both componentKey and url methods work for identifying Figma components, the URL approach has several advantages when manually creating mappings:

  1. Ease of Access: You can easily obtain the URL directly from Figma's UI by right-clicking a component and selecting "Copy Link To Selection"
  2. Human Readability: URLs contain readable information about the Figma file name and design context, making them more maintainable
  3. Direct Reference: The URL provides a clickable link back to the exact component in Figma, making it easier to check the design when updating mappings
  4. Contextual Information: The URL contains information about the file structure and location of the component, which aids in documentation

The componentKey approach, while less human-readable, is more concise and is what the CLI uses internally when generating mappings.

The mapping function has access to a rich set of properties and methods through the figma parameter.

Returns an array of all direct child nodes of the current Figma design.

figmaMapping({
  componentKey: "button-component-key",
  mapper(figma) {
    return html`<button-component>${figma.$children}</button-component>`;
  },
});

Retrieves the text content from the current Figma design node or its text children.

figmaMapping({
  componentKey: "button-component-key",
  mapper(figma) {
    return html`<button-component
      >${figma.$children[1].$textContent}</button-component
    >`;
  },
});

Maps a specific child node of the current Figma component by its layer name.

figmaMapping({
  componentKey: "dialog-component-key",
  mapper(figma) {
    return html`<div>${figma.$findOneByName("dialog")}</div>`;
  },
});

Finds the first node that meets specified criteria through a callback function.

figmaMapping({
  componentKey: "page-component-key",
  mapper(figma) {
    return html`<div>
      ${figma.$findOne((node) => node.name === "Heading")}
    </div>`;
  },
});

Traverses all child nodes and applies a function to each one.

figmaMapping({
  componentKey: "content-component-key",
  mapper(figma) {
    return figma.$visit((node) => {
      if (node.name === "Header") {
        return html`<h1>${node.$textContent}</h1>`;
      } else if (node.name === "Content") {
        return node.$textContent;
      }
    });
  },
});
figmaMapping({
  componentKey: "button-component-key",
  mapper(figma) {
    return html`
      <button-component
        .color=${figma.Color?.toLowerCase()}
        .size=${figma.Size?.toLowerCase()}
        .type=${figma.Variant?.toLowerCase()}
      >
        ${figma.$children}
      </button-component>
    `;
  },
});
figmaMapping({
  componentKey: "hero-component-key",
  mapper(figma) {
    const heading = figma.Heading;
    const supportingText = figma.$findOneByName("Supporting Text").$textContent;
    const navigation = figma.$findOneByName("Navigation");

    return html`
      <hero-component
        .heading=${heading}
        .supportingText=${supportingText}
        .navigation=${navigation}
      ></hero-component>
    `;
  },
});
import { MyButton } from "@/components/ui/button";

figmaMapping({
  componentKey: "button-component-key",
  mapper(figma) {
    // Use the text from the 2nd child ($children is zero indexed)
    const text = figma.$children[1].$textContent;

    return html`<button-component $cmp="{MyButton}">${text}</button-component>`;
  },
});

Another option is to retrieve children by their layer name:

figmaMapping({
  componentKey: "button-component-key",
  mapper(figma) {
    const text = figma.$findOneByName("Label").$textContent;

    return html`<button-component>${text}</button-component>`;
  },
});
figmaMapping({
  componentKey: "button-component-key",
  mapper(figma) {
    // Make an array to hold the CSS class names
    const classes = ["button"];

    // If figma.variant is 'primary', add the 'button-primary' class
    if (figma.variant === "primary") {
      classes.push("button-primary");
    }

    // Return a button with className applied
    return html`<button class="${classes.join(" ")}">${figma.Text}</button>`;
  },
});

For complex layouts or special case handling, you can create a generic mapper:

figmaMapping({
  genericMapper(figma) {
    if (figma.$name === "Grid row") {
      return html`<grid-component>${figma.$children}</grid-component>`;
    } else if (figma.$name === "Section") {
      return html`<section>${figma.$children}</section>`;
    }
    return undefined;
  },
});

Here's a more complex example mapping a table component:

figmaMapping({
  componentKey: "29938639a346fd304f89cbbdd9377064ac6426c1",
  mapper(figma) {
    // Extract columns data
    const columns =
      figma.$children?.map((column) => {
        const header = column.$findOneByName("Header")?.$textContent ?? "";
        return { name: header, uid: header.toLowerCase() };
      }) ?? [];

    // Extract rows data
    const firstColumn = figma.$children?.[0];
    const rowsFrame = firstColumn?.$findOneByName("Rows");
    const rowCount = rowsFrame?.$children?.length ?? 0;

    // Create rows data structure
    const rows = Array.from({ length: rowCount }, (_, rowIndex) => {
      const rowData = {};
      figma.$children?.forEach((column) => {
        const rowItem = column.$findOneByName("Rows")?.$children?.[rowIndex];
        const cellContent =
          rowItem?.$findOneByName("Row item")?.$textContent ?? "";
        const columnId =
          column.$findOneByName("Header")?.$textContent?.toLowerCase() ?? "";
        rowData[columnId] = cellContent;
      });
      return { id: rowIndex, ...rowData };
    });

    return html`
      <table-view
        aria-label="Table"
        isQuiet=${figma.Style === "Quiet"}
        selectionMode=${figma["Selection Column"] === "True"
          ? "multiple"
          : "none"}
        columns=${columns}
        rows=${rows}
      ></table-view>
    `;
  },
});

For more information on the available parameters and options for the figmaMapping function, refer to the TypeScript interfaces for component mapping.

Was this article helpful?

Product

Visual CMS

Theme Studio for Shopify

Sign up

Login

Featured Integrations

React

Angular

Next.js

Gatsby

Get In Touch

Chat With Us

Twitter

Linkedin

Careers

© 2020 Builder.io, Inc.

Security

Privacy Policy

Terms of Service

Get the latest from Builder.io

By submitting, you agree to our Privacy Policy

  • Platform Overview

  • Integrations

  • What's New

  • Figma to Code Guide

  • Composable Commerce Guide

  • Headless CMS Guide

  • Headless Commerce Guide

  • Composable DXP Guide

  • Design to Code

  • Blog

  • Knowledge Base

  • Community Forum

  • Partners

  • Templates

  • Success Stories

  • Showcase

  • Resource Center

    Glossary

© 2025 Builder.io, Inc.

Security

Privacy Policy

SaaS Terms

Security & Compliance

Cookie Preferences

Gartner Cool Vendor 2024