Fastify is a very fast, beautifully designed, and well maintained app server for Node. @enhance/fastify-plugin gives you the ability to build your frontend completely in HTML, and pure custom elements.

Fastify can be deployed to any cloud vendor that supports deploying a Node web server; the most popular choices are AWS, Azure, and GCP.


Create a project.

mkdir -p myproject
cd myproject
npm init -y
npm install fastify @enhance/fastify-plugin
touch index.mjs

Add some handy shortcuts to scripts in package.json.

  "scripts": {
    "start": "node index.mjs"

Add the following to index.mjs:

import Fastify from 'fastify'
import Enhance from '@enhance/fastify-plugin'

const app = Fastify()


app.listen({ port: 3000 }, console.log)

Create app/pages/index.html, and write some HTML!

<strong>powerful HTML here</strong>

Create some custom elements in a folder named app/elements.

export default function header ({ html }) {
  return html`<header> my cool header</header>`
export default function footer ({ html, state }) {
  return html`
      <p>footer here</p>
      <pre>${JSON.stringify(state, null, 2)}</pre>

Create app/elements.mjs to define custom element tag names.

import header from './elements/header.mjs'
import footer from './elements/footer.mjs'

let elements = {
  'my-header': header,
  'my-footer': footer

export default elements

Run npm start, and preview at http://localhost:8080.

Adding a route as plain basic HTML

Create a new page for "/about" by adding app/pages/about.html:

this is the "/about" page

Adding a route as a custom element

Sometimes we need to access state to populate a route. Custom elements are a great way to do this.

Add /fruits by creating app/pages/fruits.mjs:

export default function fruits ({ html, state }) {
  let fruit = f => `<li>${f}</li>`
  let list =
    : []
  return html`
    <h1>yummy fruit</h1>

Register the new page element by updating app/elements.mjs:

import header from './elements/header.mjs'
import footer from './elements/footer.mjs'
import fruits from './pages/fruits.mjs'

let elements = {
  'my-header': header,
  'my-footer': footer,
  'page-fruits': fruits

export default elements

Navigating to /fruits will show an empty list. Create an API route to populate it at app/api/fruits.mjs:

export async function get (req) {
  const fruits = ["apples", "mangos", "pears"]
  return {
    json: { fruits }

Reloading /fruits will show the server rendered fruit! API routes can export both get, and post handlers.

Community Resources

Visit Enhance on GitHub.
Join our Discord server to chat about development and get help from the community.
Follow Enhance in the Fediverse. We're huge fans of the IndieWeb!