Vite SSR HTML
Server-side rendering with vanilla HTML, Vite, and Nitro.
This example renders an HTML template with server-side data and streams the response word by word. It demonstrates how to use Nitro's Vite SSR integration without a framework.
├── app/
│ └── entry-server.ts
├── routes/
│ └── quote.ts
├── index.html
├── package.json
├── README.md
├── tsconfig.json
└── vite.config.ts
Overview
Add the Nitro Vite plugin to enable SSR
Create an HTML template with a <!--ssr-outlet--> comment where server content goes
Create a server entry that fetches data and returns a stream
Add API routes for server-side data
1. HTML Template
The index.html file contains an <!--ssr-outlet--> comment that marks where server-rendered content will be inserted:
<div id="quote">
<!--ssr-outlet-->
</div>
Nitro replaces this comment with the output from your server entry.
2. Server Entry
entry-server.ts
import { fetch } from "nitro";
export default {
async fetch() {
const quote = (await fetch("/quote").then((res) => res.json())) as {
text: string;
};
return tokenizedStream(quote.text, 50);
},
};
function tokenizedStream(text: string, delay: number): ReadableStream<Uint8Array> {
const tokens = text.split(" ");
return new ReadableStream({
start(controller) {
let index = 0;
function push() {
if (index < tokens.length) {
const word = tokens[index++] + (index < tokens.length ? " " : "");
controller.enqueue(new TextEncoder().encode(word));
setTimeout(push, delay);
} else {
controller.close();
}
}
push();
},
});
}
The server entry exports an object with a fetch method. It calls the /quote API route using Nitro's internal fetch, then returns a ReadableStream that emits the quote text word by word with a 50ms delay between each word.
3. API Route
quote.ts
const QUOTES_URL =
"https://github.com/JamesFT/Database-Quotes-JSON/raw/refs/heads/master/quotes.json";
let _quotes: Promise<unknown> | undefined;
function getQuotes() {
return (_quotes ??= fetch(QUOTES_URL).then((res) => res.json())) as Promise<
{ quoteText: string; quoteAuthor: string }[]
>;
}
export default async function quotesHandler() {
const quotes = await getQuotes();
const randomQuote = quotes[Math.floor(Math.random() * quotes.length)];
return Response.json({
text: randomQuote.quoteText,
author: randomQuote.quoteAuthor,
});
}
The quote route fetches a JSON file of quotes from GitHub, caches the result, and returns a random quote. The server entry calls this route to get content for the page.