Create custom components
Learn about Parcel components, a powerful way to organize your email code.
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.
You can create a custom component from your dashboard when no file is open:
From Files:
Or by searching in the Command Palette:
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.
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
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.
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!
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.
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.
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.
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">
.