Server-Side Rendering with H3 and EJS
Server-side rendering (SSR) remains a crucial technique for building performant web applications.
Today, we'll explore how to implement SSR using H3, the lightning-fast HTTP framework from UnJS, combined with EJS templating. Let's dive into creating a robust templating system that you can use in your next project.
Setting Up H3 with EJS
First, let's create a basic setup that integrates EJS with H3. You'll need to install the necessary dependencies:
npm install h3 ejs
Here's our initial template setup:
import { createApp, createRouter, eventHandler } from 'h3'
import ejs from 'ejs'
import { promises as fs } from 'fs'
import { join } from 'path'
const app = createApp()
const router = createRouter()
// Basic template rendering function
const renderTemplate = async (templatePath, data) => {
const template = await fs.readFile(templatePath, 'utf-8')
return ejs.render(template, data)
}
Creating a Template Middleware
The real power comes from creating a reusable middleware for template rendering:
const templateMiddleware = defineEventHandler(async (event) => {
event.context.render = async (template, data = {}) => {
const templatePath = join(process.cwd(), 'templates', `${template}.ejs`)
const content = await fs.readFile(templatePath, 'utf-8')
return ejs.render(content, {
...data,
// Global template helpers
helpers: {
formatDate: (date) => new Date(date).toLocaleDateString(),
capitalize: (str) => str.charAt(0).toUpperCase() + str.slice(1)
}
})
}
})
app.use(templateMiddleware)
Template Structure and Organization
Create a templates directory with this structure:
templates/
├── layouts/
│ └── main.ejs
├── partials/
│ ├── header.ejs
│ └── footer.ejs
└── pages/
├── home.ejs
└── about.ejs
Here's an example of a layout file (layouts/main.ejs):
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<%- include('../partials/header') %>
<main>
<%- content %>
</main>
<%- include('../partials/footer') %>
</body>
</html>
Implementing Routes with Templates
Now let's set up some routes that use our templates:
router.get('/', eventHandler(async (event) => {
try {
const html = await event.context.render('pages/home', {
title: 'Welcome to H3 with EJS',
content: await event.context.render('partials/home-content', {
message: 'Hello from H3!'
})
})
return html
} catch (error) {
console.error('Template rendering error:', error)
return 'Error rendering template'
}
}))
app.use(router)
Need Help Mastering H3 and Server-Side Rendering?
While this guide covers the basics of integrating EJS with H3, building production-ready applications requires deeper knowledge and experience. I offer specialized mentoring services focused on modern server-side rendering techniques, including: