Multipage SSR with React and Deno

Posted May 17, 2022 by ynwd ‐ 3 min read

In this guide you will learn how easy and clean to setup Multipage SSR in Fastro.


In previous version, you can only create one page SSR in a webapp. In this version you can make Multiple SSR Pages with simple api to set HTML title, meta, script, link, and script.

App structure

├── app.tsx
├── deno.json
├── hello
│   └── app.tsx
├── server.tsx
└── static
    ├── bundle.js
    └── hello.js

Source-code: multipage-ssr-example


Create deno configuration file: deno.json. This is used to configure TypeScript on Deno.

  "compilerOptions": {
    "strict": true,
    "jsx": "react-jsx",
    "jsxImportSource": ""

Create app and static dirs

mkdir static && mkdir hello
  • static is folder for hydrated and bundled files created automatically during app initiation. This file will be accessed by the default index.html.

  • hello is folder for hello component & page.

  • In this version, you do not need components folder again. The default folder is already set internally to ./.

Default page component

File: app.tsx.

Not like in previous versions, you can put a component file in the root of webapp directory.

// @deno-types=""
import React, { createElement as h } from "";

const App = () => {
  return (
      <h1>Hello World</h1>
      <a href="/hello">hello</a>

export default App;

Hello page component

File: hello/app.tsx.

You can create additional page components in custom directories. But you have to add a special property for the system to read it. You will find that on the next section.

// @deno-types=""
import React, { createElement as h } from "";

const App = () => {
  const [count, setCount] = React.useState(0);

  return (
      <h1>Hello World</h1>
      <button onClick={() => setCount(count + 1)}>Click the 🦕</button>
      <p>You clicked the 🦕 {count} times</p>

export default App;


Create routing file: server.tsx

import application, {
} from "";
import ssr from "";
import App from "./app.tsx";
import Hello from "./hello/app.tsx";

const appPage = ssr(<App />);
const helloPage = ssr(<Hello />).dir("./hello");

const app = application()
  .page("/", appPage, () => {
    return response()
      .title("Hello world")
  .page("/hello", helloPage, (req: Request) => {
    return response(req)
      .title("Click me")
      .meta(`name="viewport" content="width=device-width"`)
      .script(`(function (){console.log("hello")})();`)
      .style(`body {background-color: powderblue;}`)

console.log("Listening on: http://localhost:8000");

await app.serve();

In this file we create 3 endpoints:

  • / is the root webapp.
  • /hello is for hello page.
  • /static is where bundle.js and hello.js available. Both are generated automatically when you run the webapp.

Please note that for custom page/component:

  • You must append component path. In this case:
const helloPage = ssr(<Hello />).dir("./hello");
  • You must inject Request on response initiation. This is used to get the request url. This url is used to get the bundled files created on app initiation. In this case:
return response(req)

How to run locally

deno run -A --unstable server.tsx


There are 2 notes:

  • Because doesn’t support writing files, to skip bundle creation on deno deploy, set ENVIRONTMENT to PRODUCTION.


  • Because deno deploy convert .tsx file to Javascript automatically using h, you must to include it on every .tsx files.

For react component:

// @deno-types=""
import { createElement as h } from "";

For server-side module:

import { h } from "";

More info: React Without JSX.