Component shortcodes
On this page
OverviewYou can embed React, Vue, Svelte, and more into your existing templates. Let's learn how!
Prerequisites
Section titled "Prerequisites"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],
}))
}
Slinkity is designed with Vue 3 in mind. Use Vue 2.x at your own risk!
First, install Vue 3 + the Slinkity Vue renderer:
npm i -D vue@3 @slinkity/renderer-vue
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 vue = require('@slinkity/renderer-vue')
module.exports = function(eleventyConfig) {
eleventyConfig.addPlugin(slinkity.plugin, slinkity.defineConfig({
renderers: [vue],
}))
}
First, install Svelte + the Slinkity Svelte renderer:
npm i -D svelte @slinkity/renderer-svelte
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 svelte = require('@slinkity/renderer-svelte')
module.exports = function(eleventyConfig) {
eleventyConfig.addPlugin(slinkity.plugin, slinkity.defineConfig({
renderers: [svelte],
}))
}
Basic usage
Section titled "Basic usage"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' %}
{% 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:
- 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). - 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...
Choose how and when to hydrate
Section titled "Choose how and when to hydrate"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.
<!--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 %}
Liquid doesn't handle inline objects very well. So, we recommend passing each key-value pair as separate arguments as shown above. Each pair (ex.
'hydrate' true
) will be joined on our end (ex.hydrate=true
).
For a full list of options to fine-tune how and when JavaScript is loaded on the client...
💧 Learn more about partial hydration →
Pass props to shortcodes
Section titled "Pass props to shortcodes"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 %}
{% component 'Date.jsx|vue|svelte' 'date' page.date %}
Note that you can pass the hydrate
flag alongside this prop as well. hydrate
is 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}
</>
)
}
<template>
<span>{{ date }}</span>
<!--ex. only show a button when the component is hydrated-->
<button v-if="hydrate">Something interactive!</button>
</template>
<script>
export default {
props: ["date", "hydrate"],
}
</script>
<script>
export let date = '';
export let hydrate = '';
</script>
<span>{date}</span>
{#if hydrate}
<!--ex. only show a button when the component is hydrated-->
<button>Something interactive!</button>
{/if}
Pass multiple props
Section titled "Pass multiple props"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 %}
{% 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>
)
}
<template>
<details>
<summary>{{ heading }}</summary>
<slot />
</details>
</template>
<script>
export default {
props: ["heading"],
};
</script>
<script>
export let heading = "";
</script>
<details>
<summary>{heading}</summary>
<slot />
</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 %}
{% 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?