Skip to content

01 Minimum SSR

In this section, we'll look at how to render HTML with Vue App created by createApp. Full code is available at 1-min-ssr

Create Vue SSR Renderer

SSR can be achieved with just vue, but Nuxt uses vue-bundle-renderer/runtime to create a SSR Renderer. So, let's create a renderer using vue-bundle-renderer/runtime.

render.ts

ts
import { createRenderer } from 'vue-bundle-renderer/runtime'
import { renderToString } from 'vue/server-renderer'
import { h, createApp } from 'vue'

const _createApp = () => {
  const app = createApp({
    render: () => h('p', 'hello world'),
  })
  return app
}

const renderer = createRenderer(_createApp, {
  renderToString,
  manifest: {},
})

Create Render Middleware

Nitro uses h3 as HTTP server, so let's create a h3 middleware that return HTML renderd by vue ssr renderer.

render.ts

ts
export const renderMiddleware = eventHandler(async event => {
  const { res } = event.node
  const rendered = await renderer.renderToString({})
  const data = renderHTML(rendered)
  res.setHeader('Content-Type', 'text/html;charset=UTF-8')
  res.end(data, 'utf-8')
})

type Rendered = {
  html: string
  renderResourceHeaders: () => Record<string, string>
  renderResourceHints: () => string
  renderStyles: () => string
  renderScripts: () => string
}
function renderHTML({
  html,
  renderResourceHints,
  renderStyles,
  renderScripts,
}: Rendered) {
  return htmlTemplate({
    HEAD: renderResourceHints() + renderStyles(),
    APP: html + renderScripts(),
  })
}

interface HtmlTemplateParams {
  HEAD: string
  APP: string
}
function htmlTemplate({ HEAD, APP }: HtmlTemplateParams): string {
  return `
<!DOCTYPE html>
<html>
<head>
  ${HEAD}
</head>
<body>
  ${APP}
</body>
</html>
  `
}

Register Render Middleware

Finally, let's register the render middleware to the h3.

main.ts

ts
import { createServer } from 'node:http'
import { createApp, toNodeListener } from 'h3'
import { renderMiddleware } from './render'

const app = createApp()
app.use(renderMiddleware)

const server = createServer(toNodeListener(app))
server.listen(3030, () => {
  console.log('Server listening on http://localhost:3030')
})

Run Server

You can run the server with any typescript runner. In this book, we use bun.

sh
pnpm start