Skip to main content

Component shortcodes

You can embed React, Vue, Svelte, and more into your existing templates. Let's learn how!

First, install Preact + the Slinkity Preact renderer:

npm i -D preact @slinkity/preact

Then, add this renderer to a slinkity.config.js at the base of your project:

// .eleventy.js or eleventy.config.js
const slinkity = require('slinkity')
const preact = require('@slinkity/preact')

module.exports = function(eleventyConfig) {
eleventyConfig.addPlugin(
slinkity.plugin,
slinkity.defineConfig({
renderers: [preact()],
})
)
}

First, install React's suite of dependencies + the Slinkity React renderer:

npm i -D react react-dom @slinkity/react

Then, add this renderer to a slinkity.config.js at the base of your project:

// .eleventy.js or eleventy.config.js
const slinkity = require('slinkity')
const react = require('@slinkity/react')

module.exports = function(eleventyConfig) {
eleventyConfig.addPlugin(
slinkity.plugin,
slinkity.defineConfig({
renderers: [react()],
})
)
}

Using the component shortcode, you can insert components into any static template that 11ty supports.

Components must be placed in the _islands directory relative to your input directory. You can override this from the Slinkity plugin config.

Say you've written a Vue component under _islands/Component.vue. You can insert this component into your Nunjucks and Liquid templates like so:

<!--server-render only-->
{% island 'Component.vue' %}{% endisland %}

<!--server-render and hydrate client-side-->
{% island 'Component.vue', 'client:load' %}{% endisland %}

<!--render client-side only, no server rendering-->
{% clientOnlyIsland 'Component.vue' %}{% endclientOnlyIsland %}

These examples work from markdown (.md) and HTML (.html) files as well. Each use Liquid syntax by default, though this can be overridden to Nunjucks using 11ty's template override configuration.

First, this will find and load _islands/Component.vue. Note that the file extension is required (.jsx, .tsx, .svelte, etc).

Also note that client-side JS is opt-in using a client: directive. This allows you to use components for templating and only ship a JS bundle when the need arises.

💧 Learn more about partial hydration →

You can also pass data to your components using a <!--slinkity-prop FSVZqx--> shortcode.

Say you have a blog heading component written in your favorite framework (_includes/BlogHeading.jsx|vue|svelte) that uses a title and 11ty's supplied date object. You can pass these props like so, where 'date' is the prop name and page.date is the prop value:

{% island 'Date.jsx|vue|svelte' %}
{% prop 'date', page.date %}
{% prop 'title', 'My Slinktastic Blog' %}
{% endisland %}

You can access this prop inside your component like any other prop:

export default function BlogHeading({ title, date }) {
return (
<heading>
<h1>{title}</h1>
<p>Published on:<time datetime={date}>{date}</time></p>
</heading>
)
}

Any HTML inside your {% island %} shortcode will be passed as children, or the default slot when using Vue or Svelte.

Say you have a simple component to wrap text with a dropdown toggle:

export default function Dropdown({ heading, children }) {
return (
<details>
<summary>{heading}</summary>
{children}
</details>
)
}

You can pass children alongside your props like so:

{% island 'Dropdown.jsx|vue|svelte' %}
{% prop 'heading', 'Full disclosure' %}
<p>"details" and "summary" are kinda confusing element names</p>
{% endisland %}

You may be rushing to try slotted components in your markdown:

{% island 'FancyBackground.jsx|vue|svelte' %}
### Why I love markdown

- Bulleted lists are easy
- Paragraph and code blocks are even easier

{% endisland %}

🚨 Careful, this won't work as written! Paired shortcode content is processed as plain HTML, so markdown syntax won't work as expected.

There is a solution though. Since you can still nest other shortcodes as paired shortcode content, you can use 11ty's handy renderTemplate like so:

{% island 'FancyBackground.jsx|vue|svelte' %}
{% renderTemplate 'md' %}
### Why I love markdown

- Bulleted lists are easy
- Paragraph and code blocks are even easier

{% endrenderTemplate %}
{% endisland %}

Be sure to set up renderTemplate in your 11ty config before trying this. See their docs for more.

This will process your markdown, then pass the result to your component.


So injecting components into templates is nice... but what if we want to build the entire route using a component framework?

Learn about page-level components →