Skip to main content

Component shortcodes

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

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

npm i -D react react-dom @slinkity/renderer-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/renderer-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.

First, ensure your component is located in your includes directory. This defaults to _includes for new 11ty projects, but you can override this from your eleventy config.

For instance, say you've written a Vue component under _includes/Component.vue. You can insert this component into your templates like so:

{% component 'Component.vue' %}

These examples work from markdown (.md) and HTML (.html) files as well! You can use liquid syntax within either of these by default, though we recommend using Nunjucks instead. See our recommend config options for more.

This will do a few things:

  1. Find _includes/Component.vue. Note that 1) we'll always look inside the _includes directory to find your components, and 2) the file extension is required (.jsx, .tsx, .svelte, etc).
  2. Prerender your component at build time. This means you'll always see your component, even when disabling JS in your browser (try it!).

One feature we won't provide automatically: hydrating on the client. In other words, your React useState, Vue refs, or Svelte stores won't work immediately. You'll need to opt-in to sending JavaScript to the browser, which brings us to our next section...

Slinkity requires a special hydrate or renderWithoutSSR prop to ship JavaScript alongside your components. This lets you use your favorite component framework for templating guilt-free, and opt-in to JS bundles when the need arises.

To hydrate that Component.vue from earlier, you can apply the hydrate=true flag:

<!--server-render and hydrate client-side-->
{% component 'Component.vue', hydrate=true %}
<!--don't server-render and *only* render client-side-->
{% component 'Component.vue', renderWithoutSSR=true %}

If you prefer the syntax available in nunjucks templates, we do too! We recommend configuring nunjucks as the default templating language for HTML and markdown files to access this syntax everywhere. Head to our configuration docs for more details.

For a full list of options to fine-tune how and when JavaScript is loaded on the client...

💧 Learn more about partial hydration →

You can also pass data to your components as key / value pairs.

Let's say you have a date component written in your favorite framework (_includes/Date.jsx|vue|svelte) that wants to use 11ty's supplied date object. You can pass this "date" prop like so:

{% component 'Date.jsx|vue|svelte', date=page.date %}

Note that you can pass the hydrate flag alongside this prop as well. hydrate and renderWithoutSSR are considered just another prop, like any other! This should work fine for instance:

{% component 'Date.jsx|vue|svelte', date=page.date, hydrate=true %}

You can access either of these props inside your component like so:

export default function ViewDate({ date, hydrate }) {
return (
<>
<span>{date}</span>
{/* ex. only show a button when the component is hydrated */}
{hydrate ? <button>Something interactive!</button> : null}
</>
)
}

You're free to pass as many key / value pairs as you want. The names and ordering of your keys shouldn't matter, since they're all crunched into a single "props" object for your component.

{% component 'Apropcalypse.jsx|vue|svelte', date=page.date,
url=page.url, hydrate=true, fileSlug=page.fileSlug %}

Is that a "hydrate" flag sandwiched between the other props? Yep! Do we care? Nope.

Pass children / unnamed slots to component

Section titled "Pass children / unnamed slots to component"

We have a separate paired shortcode for passing child HTML: slottedComponent.

Say you have a simple component to wrap text with a dropdown toggle. That component could be written like so:

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

You can use this component the same way as the component shortcode, now with children:

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

Important gotcha: templating in children

Section titled "Important gotcha: templating in children"

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

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

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

{% endslottedComponent %}

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

But all is not lost. Since you can still nest other shortcodes as paired shortcode content, you can use 11ty's handy renderTemplate like so:

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

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

{% endrenderTemplate %}
{% endslottedComponent %}

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 →