Using components from a design library file within a plugin

I’ve seen a bunch of threads just like what I’m about ask so sorry for the repeat question. I’ve tried everything I read in the other threads and I still can’t create instances of the component in a file outside of the design library.

What I’m trying to achieve is based on the selection made it generates a specific component.
The file that I am linking to is a published design library and I have the key and the component set ID

However, I continue to get this error state in the console

unhandled promise rejection: Throw: Failed to import component set by key ""
figma.ui.onmessage = pluginMessage => {

  const designLibraryFileId = 'key';

  const componentSetId = 'ID';

  figma.importComponentSetByKeyAsync(designLibraryFileId).then((componentSet) => {

    const cartComponentSet = componentSet.findOne(node => node.type == "COMPONENT_SET" && node.name == "Cart Milestones") as ComponentSetNode;
    const defaultVariant = cartComponentSet.defaultVariant as ComponentNode;
    const m2Variant = cartComponentSet.findOne(node => node.type == "COMPONENT" && node.name == "Property 1=Cart M2") as ComponentNode;
    const m3Variant = cartComponentSet.findOne(node => node.type == "COMPONENT" && node.name == "Property 1=Cart M3") as ComponentNode;
    const m4Variant = cartComponentSet.findOne(node => node.type == "COMPONENT" && node.name == "Property 1=Cart M4") as ComponentNode;
    const m5Variant = cartComponentSet.findOne(node => node.type == "COMPONENT" && node.name == "Property 1=Cart M5") as ComponentNode;
    const m6Variant = cartComponentSet.findOne(node => node.type == "COMPONENT" && node.name == "Property 1=Cart M6") as ComponentNode;

    if(pluginMessage.radioState === "cartM1"){
      defaultVariant.createInstance();
    } else if(pluginMessage.radioState === "cartM2"){
      m2Variant.createInstance();
    } else if(pluginMessage.radioState === "cartM3"){
      m3Variant.createInstance();
    } else if(pluginMessage.radioState === "cartM4"){
      m4Variant.createInstance();
    } else if(pluginMessage.radioState === "cartM5"){
      m5Variant.createInstance();
    } else if(pluginMessage.radioState === "cartM6"){
      m6Variant.createInstance();
    }  
    
    figma.closePlugin();
  }

  )}

Any help would be greatly appreciated.

You need to import a specific component set, not the whole library file. And it’s done by component set KEY, not ID.

In general plugins don’t have access to file keys — unless it’s a private plugin but in that case the key is not used in the plugin context, it’s used to communicate with the REST API.

Docs: figma | Plugin API

Thanks for the reply. This is for a private internal plugin, so does that change what you called out?

Edit: I made the update you called out but I’m now getting a different error state. Sorry if these are basic questions, I’m very new to typescript.

unhandled promise rejection: TypeError: cannot read property 'defaultVariant' of null

This is the latest code I am using.

figma.ui.onmessage = pluginMessage => {

  // Define the key of the component set
  const cartMilestonesSet = 'key';

  figma.importComponentSetByKeyAsync(cartMilestonesSet).then((componentSet) => {

    const cartComponentSet = componentSet.findOne(node => node.type == "COMPONENT_SET" && node.name == "Cart Milestones") as ComponentSetNode;
    const m1Variant = cartComponentSet?.defaultVariant as ComponentNode;
    const m2Variant = cartComponentSet?.findOne(node => node.type == "COMPONENT" && node.name == "Property 1=Cart M2") as ComponentNode;
    const m3Variant = cartComponentSet?.findOne(node => node.type == "COMPONENT" && node.name == "Property 1=Cart M3") as ComponentNode;
    const m4Variant = cartComponentSet?.findOne(node => node.type == "COMPONENT" && node.name == "Property 1=Cart M4") as ComponentNode;
    const m5Variant = cartComponentSet?.findOne(node => node.type == "COMPONENT" && node.name == "Property 1=Cart M5") as ComponentNode;
    const m6Variant = cartComponentSet?.findOne(node => node.type == "COMPONENT" && node.name == "Property 1=Cart M6") as ComponentNode;

    if(pluginMessage.radioState === "cartM1"){
      console.log('m1')
      // m1Variant.createInstance();
    } else if(pluginMessage.radioState === "cartM2"){
      console.log('m2')
      // m2Variant.createInstance();
    } else if(pluginMessage.radioState === "cartM3"){
      console.log('m3')
      // m3Variant.createInstance();
    } else if(pluginMessage.radioState === "cartM4"){
      console.log('m4')
      // m4Variant.createInstance();
    } else if(pluginMessage.radioState === "cartM5"){
      console.log('m5')
      // m5Variant.createInstance();
    } else if(pluginMessage.radioState === "cartM6"){
      console.log('m6')
      // m6Variant.createInstance();
    }  
    
    figma.closePlugin();
  }

  )}

All of the console.log messages print correctly so I think all the logic is correct. But when I try to create an instance of any of these I get the error

[symbol] failed to repair styles
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'defaultVariant')

@Gleb would you be able to provide any additional help here?

Are you trying to grab a component from the library or the whole library?
If you are trying to grab a specific component from a library you can use this code after you get the component key from the console:

in Figma go to your component and select it, go to Plugins >>Development>>Open Console and paste the following code in at the console prompt and it will return your component key:

figma.currentPage.selection[0].key

Copy the key and replace this text - [insert component key here] - in the code below and use that in your plugin.

async function importNode() {
  let importComponent = await figma.importComponentByKeyAsync("[insert component key here]");
  let instance = importComponent.createInstance();
  await figma.currentPage.appendChild(instance);
}

importNode();

You are getting an error in this line. You already imported the component (componentSet) and you are trying to search for it inside of itself. So you are getting undefined as a component set doesn’t (and can’t) contain any other component sets. Just delete these lines and rename componentSet (after then) to cartComponentSet.

Be careful with using type assertions like as ComponentSetNode. As you can see it made TypeScript believe everything is fine as you told it you know that you will be getting a component set but that wasn’t the case when running the code.

Also instead of using cardComponentSet.findOne it’s better to use cardComponentSet.children.find(n => n.name === 'Property 1=Cart M2') as this would already give you the correct type (no need for type assertion and checking the type — component set can only contain component nodes). Plus it won’t search the whole tree of the component set contents which is very slow and unnecessary.

No. You still won’t be using the file key in this context and there is no possibility to import the whole library at once — you need to import components and component sets by keys individually if you need any additional ones to the one you are using here.


Note that while the function @Banry_Lincoln_Frisco provided is correct, it only works to import a singular component, not a component set (variant set). Also you don’t need to await the appendChild method result as it’s a synchronous function, plus you don’t need this line in the function at all as createInstance method already places an instance on the current page.

1 Like

@Gleb good to know. I am successfully importing a component set but… I just noticed, it is contained within a component. This may be the source of another issue. LOL

@Gleb and @Banry_Lincoln_Frisco thank you so much for all of this info.

I added your suggestions and it is now working. Truly appreciate both your help with this problem.

final code

figma.showUI(__html__);

figma.ui.resize(680,200);

figma.ui.onmessage = pluginMessage => {

  // Define the key of the component set
  const cartMilestonesSet = 'key';

  figma.importComponentSetByKeyAsync(cartMilestonesSet).then((cartComponentSet) => {

    const m1Variant = cartComponentSet.defaultVariant as ComponentNode;
    const m2Variant = cartComponentSet.children.find(n => n.name === 'Property 1=Cart M2') as ComponentNode;
    const m3Variant = cartComponentSet.findOne(node => node.type == "COMPONENT" && node.name === "Property 1=Cart M3") as ComponentNode;
    const m4Variant = cartComponentSet.findOne(node => node.type == "COMPONENT" && node.name === "Property 1=Cart M4") as ComponentNode;
    const m5Variant = cartComponentSet.findOne(node => node.type == "COMPONENT" && node.name === "Property 1=Cart M5") as ComponentNode;
    const m6Variant = cartComponentSet.findOne(node => node.type == "COMPONENT" && node.name === "Property 1=Cart M6") as ComponentNode;


    if(pluginMessage.radioState === "cartM1"){
      m1Variant.createInstance();
    } else if(pluginMessage.radioState === "cartM2"){
      m2Variant.createInstance();
    } else if(pluginMessage.radioState === "cartM3"){
      m3Variant.createInstance();
    } else if(pluginMessage.radioState === "cartM4"){
      m4Variant.createInstance();
    } else if(pluginMessage.radioState === "cartM5"){
      m5Variant.createInstance();
    } else if(pluginMessage.radioState === "cartM6"){
      m6Variant.createInstance();
    }  
    
    figma.closePlugin();
  }

  )}
2 Likes

I have a related question.

I have finished my initial plugin that retrieves and places a specific Component Set (name: ‘Cover’) from my team library with the selected variant and data from the UI. (The ‘Cover’ component set has variants for the status of the project, like ‘In Progress’ & ‘Done’. When the ‘Cover’ is initially placed the user selects a “Status” from a dropdown menu and then that instance of the ‘Cover’ is placed)

I have now modified the plugin to first check and see if the component (‘Cover’) is on the page. If ‘Cover’ exists on the page it is selected and then it gets all the data from the nodes to repopulate the UI so the user can update the ‘Cover’ data.

I am able to place a new instance with the updated info but I am unable to change the instance of the existing component on the page.

After selecting the current instance, lets say the current instance has the project status set to ‘In Progress’, of the component set how do I change it to "Done’?

THANK YOU! Let me give that a try.
I hadn’t even considered that. I am a designer not a developer. lol
I’ll let you know if that works. :slight_smile:

So when I check ID’s of the different instances from the placed component (not the original) from the Figma console using figma.currentPage.selection[0].id i get the same is ‘71:362’ for each one. And these ID’s are different from the original?

For reference, variant is titled as such:

'In Progress': 'Project Status=In Progress',
'Sprint Ready': 'Project Status=Sprint Ready',
'Done': 'Project Status=Done',
'Sandbox': 'Project Status=Sandbox',
'FigJam': 'Project Status=Figjam',
'Assets': 'Project Status=Assets',

I am a little out of my depth.

The ID has a unique value for each node. But I don’t understand why you need an ID to change the variant.

First thing I did was read the documentation around the snippet you sent and it said I had to use an ID, but if not then would it look like this?

InstanceNode.setProperties({'In Progress': 'Project Status=Done});

Is it that simple? :smiley:

The documentation says the following:

should be suffixed with ‘#’ and a unique ID for ‘TEXT’, ‘BOOLEAN’, and ‘INSTANCE_SWAP’ properties

This is not the ID you can get with node.id. This is the ID of the property of the component, which is contained in the name of this property. That is, it exists in the property name, and nothing needs to be added.

And, as you may have noticed, the ‘VARIANT’ type is not specified.

Yes, that’s how it is for the ‘VARIANT’ type. Just don’t forget to add a closing quote (apostrophe) for the value.

I am still missing something, I did add the '.

I was able to get the componentPropertyDefintions when I run the plug in and place the Instance. They are as follows:

Definitions

  1. {Project Status: {…}}

  2. Project Status:

1. defaultValue: "In Progress"
2. type: "VARIANT"
3. variantOptions: Array(6)

  1. 0: "In Progress"
  2. 1: "Sprint Ready"
  3. 2: "Sandbox"
  4. 3: "Done"
  5. 4: "Figjam"
  6. 5: "Assets"
  7. length: 6

so

projectStatus.setProperties({'Project Status': 'Done'});

Does not work. I am working with an InstanceNode named Cover.
I am sure this is something so simple that I am not understanding. Thank you for helping.

What do you mean it doesn’t work? Are there any errors in the console?

Yes, so sorry.

Error: in setProperties: Could not find a component property with name: ‘Project Status’
at InstanceNode.setProperties (eval at (eval at createScopedEvaluatorFactory

Check the property name.