SSR Stream Interception
How to intercept SSR stream to inject content before it is sent to the client.
Additional Content Injection in SSR
In some cases you may want to inject additional content into the SSR stream before it's sent to the client. This can be useful for injecting properties onto the document's root element (e.g., the lang attribute or a class name based on the user's request headers).
Step 1: Create a custom middleware
Create a new file in src/middleware/. Waku automatically discovers and loads middleware from this directory.
// ./src/middleware/stream-interceptor.ts
import type { MiddlewareHandler } from 'hono';
import { unstable_getContextData as getContextData } from 'waku/server';
const streamInterceptor = (): MiddlewareHandler => {
return async (c, next) => {
const data = getContextData<{ theme?: string }>();
await next();
const contentType = c.res.headers.get('content-type');
const isDocument = contentType?.includes('text/html');
const lang = 'en-US';
if (isDocument && c.res.body) {
const newBody = c.res.body.pipeThrough(
new TransformStream({
transform(chunk, controller) {
const text = new TextDecoder().decode(chunk);
const newText = text.replace(
'<html>',
`<html lang="${lang}" ${data.theme === 'dark' ? "class='dark'" : ''}>`,
);
controller.enqueue(new TextEncoder().encode(newText));
},
}),
);
c.res = new Response(newBody, {
status: c.res.status,
headers: c.res.headers,
});
}
};
};
export default streamInterceptor;Step 2: Verify the changes
After restarting the server, you can verify that the changes have taken effect by inspecting the HTML source of your page. You should see the lang attribute and, if your system is set to dark mode, the class attribute on the <html> element as well.

