Server Side Rendering Draft #
Following this tutorial you will learn how to server side render Duet Design System’s Web Components using Eleventy and other static site generators.
For this particular tutorial we’re assuming you already have a working Eleventy website up and running with Duet’s Fonts and CSS Framework. If you aren’t familiar with Eleventy, its documentation provides easy to follow getting started instructions.
Getting started #
To get started with server side rendering, install Duet’s Web Components and NCP into your own project by running:
npm install @duetds/components ncp --save-dev
Once installed, add a script to your package.json that copies the component library from Duet’s package into a location you’ve specified:
"scripts": {
"copy:components": "ncp ./node_modules/@duetds/components/lib ./src/js/components"
}
In the above example we’re copying the components into src/js/components
directory. You can now call this script while starting up your app or build process to make sure you’ve always got the latest components copied over:
"start": "copy:components & dev"
Once you have a copy task in place and have copied the component library over, you can put script tags similar to these in the <head>
of your page:
<script type="module" src="/js/components/duet/duet.esm.js"></script>
<script nomodule src="/js/components/duet/duet.js"></script>
The above scripts will take care of client side hydration of the server side rendered components, nothing else is required.
SSR configuration #
Now that we’re done with the client side hydration, it’s time to configure Eleventy to render our Web Components on server.
Duet’s Web Components package includes a hydrate app for this usage. The hydrate app is a bundle of the same components, but compiled so that they can be hydrated on a Node.js server and generate static HTML and CSS.
To get started, import the hydrate app into your .eleventy.js
configuration:
const hydrate = require("@duetds/components/hydrate")
Next, add the following transform into your .eleventy.js
configuration file that takes content as an input and processes it using Duet’s hydrate app:
const hydrate = require("@duetds/components/hydrate")
module.exports = function(config) {
config.addTransform("hydrate", async (content, outputPath) => {
if (outputPath.endsWith(".html")) {
try {
const results = await hydrate.renderToString(content, {
clientHydrateAnnotations: true,
removeScripts: false,
removeUnusedStyles: false
})
return results.html
} catch (error) {
return error
}
}
return content
})
}
Most of the time you probably only want to do the rendering in production environment, so you could modify the above to include a check for that:
const hydrate = require("@duetds/components/hydrate")
module.exports = function(config) {
config.addTransform("hydrate", async (content, outputPath) => {
if (process.env.ELEVENTY_ENV == "production") {
if (outputPath.endsWith(".html")) {
try {
const results = await hydrate.renderToString(content, {
clientHydrateAnnotations: true,
removeScripts: false,
removeUnusedStyles: false
})
return results.html
} catch (error) {
return error
}
}
}
return content
})
}
The above transform now gives you server side rendered components that function without JavaScript. Please note that you need to separately pre-render the content for each theme you want to support.
One thing to also keep in mind is that if you’re doing HTML minification, you should configure it so that it won’t remove comments from HTML as the client side hydration relies on them to be available. If you’re using html-minifier
with Eleventy, you can configure it like this:
const htmlmin = require("html-minifier")
module.exports = function(config) {
config.addTransform("htmlmin", function(content, outputPath) {
if (process.env.ELEVENTY_ENV == "production") {
if (outputPath.endsWith(".html")) {
let minified = htmlmin.minify(content, {
// Client side hydration won’t work if comments are removed
removeComments: false
})
return minified
}
}
return content
})
}