Use a template engine

In this guide you will learn how to use a template engine, like EJS and Handlebars, to create server-rendered HTML views.

Related example
Using EJS as a template engine with Encore.ts
$ encore app create --example=ts/template-engine

Serving a specific template file

Breakdown of the example:

  • We import a NPM package for rendering templates, in this case EJS.
  • We have a Raw Endpoint to handle template rendering, in this case we are serving a specific template file (person.html) under /person.
  • We make use of the EJS to render the template with the given data.
  • We set the content-type header to text-html and then respond with the generated HTML.
template/template.ts
template/views/person.html
import { api } from "encore.dev/api"; import ejs, { Options } from "ejs"; const BASE_PATH = "./template/views"; const ejsOptions: Options = { views: [BASE_PATH] }; export const serveSpecificTemplate = api.raw( { expose: true, path: "/person", method: "GET" }, async (req, resp) => { const viewPath = `${BASE_PATH}/person.html`; const html = await ejs.renderFile( viewPath, // Supplying data to the view { name: "Simon" }, ejsOptions, ); resp.setHeader("content-type", "text/html"); resp.end(html); }, );

Serving from a dynamic path

This example is similar to the one above, but in this case we use a fallback path to serve a template file based on the path. We use the currentRequest function to get the path and then render the template file based on the path. If no path is provided, we default to index.html.

import { api } from "encore.dev/api"; import { APICallMeta, currentRequest } from "encore.dev"; import ejs, { Options } from "ejs"; const BASE_PATH = "./template/views"; const ejsOptions: Options = { views: [BASE_PATH] }; export const servePathTemplate = api.raw( { expose: true, path: "/!path", method: "GET" }, async (req, resp) => { const { path } = (currentRequest() as APICallMeta).pathParams; const viewPath = `${BASE_PATH}/${path ?? "index"}.html`; const html = await ejs.renderFile(viewPath, ejsOptions); resp.setHeader("content-type", "text/html"); resp.end(html); }, );

Serving inline HTML

In this example we are serving inline HTML with EJS. We use the ejs.render function to render the inline HTML with the given data.

import { api } from "encore.dev/api"; import ejs, { Options } from "ejs"; const inlineHTML = ` <!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="stylesheet" href="/public/styles.css" > </head> <body> <h1>Static Inline HTML Example</h1> <h1>Name: <%= name %>!</h1> </body> </html> `; export const serveInlineHTML = api.raw( { expose: true, path: "/html", method: "GET" }, async (req, resp) => { const html = ejs.render(inlineHTML, { name: "Simon" }); resp.setHeader("Content-Type", "text/html"); resp.end(html); }, );

Static files

In the above example we are fetching a stylesheet from the /public path. We can use the api.static function to serve all files in the ./assets directory under the /public path prefix:

// Serve all files in the ./assets directory under the /public path prefix. export const assets = api.static({ expose: true, path: "/public/*path", dir: "./assets", });

Learn more about serving static files in the Static Files guide.