How to Assign Component Property to a Text Node within Variants

Hello everyone,

I am developing a Figma plugin where I have created a ComponentSet with two variants. Each variant has a state and a heading property. I want to dynamically assign the heading property to a text node within each variant.

Here is the simplified code for creating the ComponentSet:

figma.showUI(__html__, {
  height: 200,
  width: 400,
});

figma.ui.onmessage = async (msg) => {
  if (msg.type === 'create-input-field') {
    await figma.loadFontAsync({ family: 'Inter', style: 'Regular' });
    // Create variants for the component
    const defaultComponent = createComponentWithFrame('State=Default');
    const filledComponent = createComponentWithFrame('State=Filled');
    // Create a component set and add the components as variants
    const componentSet = figma.combineAsVariants([defaultComponent, filledComponent], figma.currentPage);
    componentSet.name = 'My Component Set';
    componentSet.resizeWithoutConstraints(1200, componentSet.height);
    componentSet.layoutMode = 'HORIZONTAL';
    componentSet.counterAxisSizingMode = 'AUTO';
    componentSet.primaryAxisSizingMode = 'AUTO';
    componentSet.paddingTop = 20;
    componentSet.paddingBottom = 20;
    componentSet.paddingLeft = 20;
    componentSet.paddingRight = 20;
    componentSet.itemSpacing = 18;
    componentSet.fills = [];
    componentSet.cornerRadius = 5;
    componentSet.strokes = [{ type: 'SOLID', color: { r: 0.592, g: 0.278, b: 1.0 } }]; // #9747ff
    componentSet.strokeWeight = 1;
    componentSet.dashPattern = [10, 5];
    componentSet.strokeAlign = 'INSIDE';
    componentSet.strokeJoin = 'MITER';
    componentSet.strokeMiterLimit = 16;
    componentSet.clipsContent = true;

    // Position the component set on the canvas
    componentSet.x = figma.viewport.center.x - componentSet.width / 2;
    componentSet.y = figma.viewport.center.y - componentSet.height / 2;
  }
};

const createComponentWithFrame = (variant: string): ComponentNode => {
  let color: Paint[] = [{ type: 'SOLID', color: { r: 0.592, g: 0.278, b: 1.0 } }];
  switch (variant) {
    case 'State=Filled':
      color = [{ type: 'SOLID', color: { r: 0.9, g: 0.9, b: 0.9 } }];
      break;
    default:
      color = [{ type: 'SOLID', color: { r: 0.592, g: 0.278, b: 1.0 } }];
      break;
  }

  const component = figma.createComponent();
  component.name = variant;
  component.layoutMode = 'VERTICAL';
  component.fills = color;
  component.resizeWithoutConstraints(252, component.height);

  component.addComponentProperty('Heading', 'TEXT', '');
  setupAutoLayoutAndText(component);

  return component;
};

const setupAutoLayoutAndText = (node: BaseFrameMixin): void => {
  node.layoutMode = 'VERTICAL';
  node.primaryAxisSizingMode = 'AUTO';
  node.counterAxisSizingMode = 'AUTO';
  node.paddingTop = 0;
  node.paddingBottom = 0;
  node.paddingLeft = 0;
  node.paddingRight = 0;
  node.itemSpacing = 4;

  const label = figma.createText();
  label.characters = 'Label';
  label.fontName = { family: 'Inter', style: 'Regular' };
  label.lineHeight = {
    value: 23,
    unit: 'PIXELS',
  };
  label.letterSpacing = { value: 0, unit: 'PERCENT' };
  label.textAlignHorizontal = 'LEFT';
  label.textAlignVertical = 'BOTTOM';
  label.fontSize = 14;
  label.name = 'Label';

  const labelContainer = figma.createFrame();
  const childContainer = figma.createFrame();

  labelContainer.counterAxisAlignItems = 'CENTER';
  labelContainer.counterAxisSizingMode = 'AUTO';
  labelContainer.primaryAxisAlignItems = 'MIN';
  labelContainer.primaryAxisSizingMode = 'FIXED';
  labelContainer.fills = [];
  labelContainer.strokes = [];
  labelContainer.paddingTop = 0;
  labelContainer.paddingBottom = 0;
  labelContainer.paddingLeft = 0;
  labelContainer.paddingRight = 0;
  labelContainer.itemSpacing = 4;
  labelContainer.clipsContent = false;
  labelContainer.layoutAlign = 'STRETCH';
  labelContainer.layoutGrow = 0;
  labelContainer.layoutMode = 'HORIZONTAL';
  labelContainer.layoutGrids = [];
  labelContainer.layoutPositioning = 'AUTO';
  labelContainer.appendChild(label);

  node.appendChild(labelContainer);
  node.appendChild(childContainer);
};


I am looking to understand how I can link the heading property to the text node so that changes to the property reflect in the text content.


Any guidance or examples on how to achieve this would be greatly appreciated.

Thank you!

Hi @tank666 Thank you for your response! I appreciate the guidance on using the componentPropertyReferences property to link the heading property to the text node.

Here’s the updated code snippet that worked for me:


figma.showUI(__html__, {
  height: 200,
  width: 400,
});

figma.ui.onmessage = async (msg) => {
  if (msg.type === 'create-input-field') {
    await figma.loadFontAsync({ family: 'Inter', style: 'Regular' });
    // Create variants for the component
    const defaultComponent = createComponentWithFrame('State=Default');
    const filledComponent = createComponentWithFrame('State=Filled');
    // Create a component set and add the components as variants
    const componentSet = figma.combineAsVariants([defaultComponent, filledComponent], figma.currentPage);
    componentSet.name = 'My Component Set';
    componentSet.resizeWithoutConstraints(1200, componentSet.height);
    componentSet.layoutMode = 'HORIZONTAL';
    componentSet.counterAxisSizingMode = 'AUTO';
    componentSet.primaryAxisSizingMode = 'AUTO';
    componentSet.paddingTop = 20;
    componentSet.paddingBottom = 20;
    componentSet.paddingLeft = 20;
    componentSet.paddingRight = 20;
    componentSet.itemSpacing = 18;
    componentSet.fills = [];
    componentSet.cornerRadius = 5;
    componentSet.strokes = [{ type: 'SOLID', color: { r: 0.592, g: 0.278, b: 1.0 } }]; // #9747ff
    componentSet.strokeWeight = 1;
    componentSet.dashPattern = [10, 5];
    componentSet.strokeAlign = 'INSIDE';
    componentSet.strokeJoin = 'MITER';
    componentSet.strokeMiterLimit = 16;
    componentSet.clipsContent = true;

    // Position the component set on the canvas
    componentSet.x = figma.viewport.center.x - componentSet.width / 2;
    componentSet.y = figma.viewport.center.y - componentSet.height / 2;
  }
};

const createComponentWithFrame = (variant: string): ComponentNode => {
  let color: Paint[] = [{ type: 'SOLID', color: { r: 0.592, g: 0.278, b: 1.0 } }];
  switch (variant) {
    case 'State=Filled':
      color = [{ type: 'SOLID', color: { r: 0.9, g: 0.9, b: 0.9 } }];
      break;
    default:
      color = [{ type: 'SOLID', color: { r: 0.592, g: 0.278, b: 1.0 } }];
      break;
  }

  const component = figma.createComponent();
  component.name = variant;
  component.layoutMode = 'VERTICAL';
  component.fills = color;
  component.resizeWithoutConstraints(252, component.height);

  const headingProperty = component.addComponentProperty('Heading', 'TEXT', '');
  setupAutoLayoutAndText(component, headingProperty);

  return component;
};

const setupAutoLayoutAndText = (node: BaseFrameMixin, headingProperty: string): void => {
  node.layoutMode = 'VERTICAL';
  node.primaryAxisSizingMode = 'AUTO';
  node.counterAxisSizingMode = 'AUTO';
  node.paddingTop = 0;
  node.paddingBottom = 0;
  node.paddingLeft = 0;
  node.paddingRight = 0;
  node.itemSpacing = 4;

  const label = figma.createText();
  label.characters = 'Label';
  label.fontName = { family: 'Inter', style: 'Regular' };
  label.lineHeight = {
    value: 23,
    unit: 'PIXELS',
  };
  label.letterSpacing = { value: 0, unit: 'PERCENT' };
  label.textAlignHorizontal = 'LEFT';
  label.textAlignVertical = 'BOTTOM';
  label.fontSize = 14;
  label.name = 'Label';

  const labelContainer = figma.createFrame();
  const childContainer = figma.createFrame();

  labelContainer.counterAxisAlignItems = 'CENTER';
  labelContainer.counterAxisSizingMode = 'AUTO';
  labelContainer.primaryAxisAlignItems = 'MIN';
  labelContainer.primaryAxisSizingMode = 'FIXED';
  labelContainer.fills = [];
  labelContainer.strokes = [];
  labelContainer.paddingTop = 0;
  labelContainer.paddingBottom = 0;
  labelContainer.paddingLeft = 0;
  labelContainer.paddingRight = 0;
  labelContainer.itemSpacing = 4;
  labelContainer.clipsContent = false;
  labelContainer.layoutAlign = 'STRETCH';
  labelContainer.layoutGrow = 0;
  labelContainer.layoutMode = 'HORIZONTAL';
  labelContainer.layoutGrids = [];
  labelContainer.layoutPositioning = 'AUTO';
  labelContainer.appendChild(label);

  node.appendChild(labelContainer);
  label.componentPropertyReferences = { characters: headingProperty };
  node.appendChild(childContainer);
};

Thank you again for your help!