Create custom components

Learn about Parcel components, a powerful way to organize your email code.


Tip

If you started using Parcel on or after April 23, 2024, you're already set up to create our latest, custom components! Otherwise, check out the differences between our legacy and latest implementation to see if you're ready to upgrade.

Custom components are a way to organize your code into reusable blocks. Unlike snippets, when you change the component file, it will change in all places it has been used.

To get the most out of custom components, check out our Visual editor overview. How you create a component directly affects how you and your team can modify components in the visual editor.

Create a component file

You can create a custom component from your dashboard when no file is open:

Image of a Parcel dashboard while no file is open. In the middle of the screen is a row of actions: create an email, create a template, and create a component. A red arrow points to the last option.
Click "Create a component" on the right side of your dashboard.

From Files:

An image of the top left of the Parcel window. Beside the folder titled Files, the plus icon has been clicked. A menu of options shows. The option New Component is highlighted in red.
Click "+" then "New Component."

Or by searching in the Command Palette:

A recordig of the entire Parcel window. The user clicks the top-left settings dropdown. The user clicks the first option: Command Palette. In the center of the screen, a list of commands appears with the option to search. The user types "compon" and the command "New component" appears.
Click Cntl/Cmd + Shift + P or click Command Palette from the sidebar settings dropdown.

The name you give the file will become the Component Name (removed of spaces), which you'll use to insert your component into your messages. You can change this at any time at the top of your component file.

At the top is the component name. Under that is the code editor.
Image of open component file

The basics

There are two required tags when setting up a component: <script> and <template>. The <script> tag is where you set properties, slots, and other Javascript logic. The <template> tag is where you create your content and reference variables and slots.

When you create a component from scratch in Parcel, this is the starting code we provide:

<!-- 
  Insert this component in your email with the following code:
  
  <my-paragraph></my-paragraph>
-->
<script>
    export const config = { label: "my-paragraph" }
</script>
<template>
  <slot>Content goes here</slot>
</template>

In the script, config gives the component a label by setting "label": "my-paragraph",.

You can make the label different from the component name, but we recommend you keep them similar. The component name is used in the code, which comes with some restrictions like no spaces. The label appears in the visual editor and comes with fewer restrictions if you want a more user-friendly name there.

There are four other tags you can include in your top-level elements, as well:

  • <style> - where you establish the style of the component; you can render variables created in the <script> tag
  • <title> - use this to insert a title element into the <head> of the email
  • <link> - use this to insert a link element into the <head> of the email
  • <meta> - use this to insert a meta element into the <head> of the email

Define relationships between components

You can also definte parent/child relationships between components such that team members using the visual editor can only drag/drop specific components into each other.

For instance, maybe you have a component social-icons that team members should only be able to insert into your footer component. In this case, the config code of the social-icons component should include:

<script>
    export const config = { 
      label: "social-icons",
      allowedParents: ['footer']
    }
</script>

If you want to specify that a component can only have certain child components, take advantage of slots, which let you create modifiable, placeholder content.

The config code would include this:

<script>
  export const slots = Component.defineSlots({
      default: {
        schema: Component.slots.children(['child-component-label']),
      },
  });
</script>

And the template code would include this:

<template>
  <div><slot>Content goes here</slot></div>
</template>

Check out Validating slots for more info on child components.

Add your content

If you're creating a component that should be the same from message-to-message, you can add HTML directly to the <template> tag. For instance, maybe you're making a footer that should be static across all your emails. On the other hand, perhaps you want to create a component with placeholder content that you add to any message then your team can modify the placeholder to be different from message-to-message. If that's the case, add a slot!

Slots: create modifiable, placeholder text

Once you have the basic component, you can add placeholder text that your team can modify in any message the component is inserted in. This is done through slots. You'll define your slots in the <script> tag and insert <slot> tags in the <template> tag where you want your team to create different text from message-to-message.

<script>
  export const config = {
    "label": "My paragraph",
  };
  export const slots = Component.defineSlots({
    default: {
      schema: Component.slots.text(),
      marks: ['bold', 'italic', 'underline', 'strikethrough', 'link', 'color']
    },
  });
</script>
<template>
  <p>
    <slot>This is a paragraph</slot>
  </p>
</template>

In the <script> tag above, we added:

export const slots = Component.defineSlots({
    default: {
      schema: Component.slots.text(),
      marks: ['bold', 'italic', 'underline', 'strikethrough', 'link', 'color']
    },
  })

This defines the slot and will export it for use in the visual editor. In this example, we're only adding one slot, so we don’t need to name it. We’ll just use the default name that is default.

Inside the slot script, we need to say what type of slot this is. We're using this one for text, so we added schema: Component.slots.text(),. When using text slots, you can also optionally allow a set of marks to be applied. This will give your team some pop-up options when they highlight text in the visual editor.

The text "paragraph" is highlighted in the visual editor. Above that are a series of properties you can choose to style the text. For a slot, this is made possible when you set marks in the <script> tag.
In the visual editor, you can highlight a slot and see properties when marks are set in the <script> tag.

Now, move down to the <template> tag of our example. This is where you add the <slot> element. In this example, I added some text as placeholder. This will show as default in messages until your team changes it.

<template>
  <p>
    <slot>This is a paragraph</slot>
  </p>
</template>

After you insert the component in a message, you can edit the slot directly in the code editor.

<my-paragraph>Hello this is meant to replace the placeholder text.</my-paragraph>

In the visual editor, click the slot (note the name will match the label you set in the <script> tag) then edit the text.

Add properties

The next step in enhancing the component is to add properties. These properties allow for a lot more customization of the component. Here we’ll add some text styling.

<script>
  export const config = {
    "label": "My paragraph",
  };
  export const slots = Component.defineSlots({
    default: {
      schema: Component.slots.text(),
      marks: ['bold', 'italic', 'underline', 'strikethrough', 'link', 'color']
    },
  });
  export const props = Component.defineProps({
    color: {
      section: 'Text',
      label: 'Color',
      schema: Component.props.string().optional(),
      type: 'color',
    },
    'font-size': {
      section: 'Text',
      label: 'Size',
      schema: Component.props.number().default(16),
      type: 'number',
      min: 14,
      max: 30,
    },
    'text-align': {
      section: 'Text',
      label: 'Align',
      schema: Component.props.enum(['left', 'center', 'right']).optional(),
      type: 'toggle',
      options: [
        { label: 'Left', value: 'left', icon: 'format_align_left' },
        { label: 'Center', value: 'center', icon: 'format_align_center' },
        { label: 'Right', value: 'right', icon: 'format_align_right' },
      ],
    }
  });
  const styleObject = {
    color: props.color,
    'font-size': props['font-size'] + 'px',
    'text-align': props['text-align'],
    'line-height': '150%',
  };
</script>
<template>
  <p #set:style="styleObject">
    <slot>This is a paragraph</slot>
  </p>
</template>

First, we need to define our properties in a similar way to how we defined our slot by using export const props = Component.defineProps({}).

We’re adding three properties here: color, font-size and text-align. Each has a number of additional settings. These are all optional, but we recommend you set some of these to help improve the user experience in the visual editor.

Create a style object

After you set your properties, you'll create another object to help build the component and pass styles to your message. Here we'll call it the styleObject, but you can name this anything you like. Then, when you add a style attribute to your template, we will automatically pass this Javascript object to output your styles.

  const styleObject = {
    color: props.color,
    'font-size': props['font-size'] + 'px',
    'text-align': props['text-align'],
    'line-height': '150%',
  };

The names on the left are the CSS attributes you want to include, and on the right are the values. If a property is not set, it will have a value of undefined and will not be included in the output code. You can also include hard-coded styles which aren’t included in the props, like line-height in the example above.

To add this to our component, let's set the style of the paragraph tag: <p #set:style="styleObject">.