Quick Start

From zero to a running server in under 5 minutes.

Prerequisites

You need Bun installed. That's the only dependency.

curl -fsSL https://bun.sh/install | bash

1. Create your project

Make a folder and initialize it:

mkdir my-site && cd my-site
bun init -y
bun add index97

2. Create the server entry point

Create server.js:

import { createServer } from 'index97'

createServer({
  pagesDir: './pages',
  port: 3000,
  dev: true
})

3. Create your first layout

Create pages/_layout.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{slot:title || My Site}}</title>
</head>
<body>
  <nav><a href="/index97/">Home</a></nav>
  <main>{{content}}</main>
</body>
</html>

4. Create your home page

Create pages/index.phtml:

<template data-slot="title">Welcome — My Site</template>

<h1>Hello, world!</h1>
<p>Building websites like it's 1997.</p>

5. Run it

bun server.js

Open http://localhost:3000. Your page renders inside the layout automatically.

Hot reload is on. With dev: true, any file change instantly updates the browser. CSS changes re-stamp without a full reload.

6. Add a handler

Pair a .js file with your .phtml to provide data. Create pages/hello.js:

export async function GET(req) {
  const name = new URL(req.url).searchParams.get('name') ?? 'world'
  return { name }
}

And pages/hello.phtml:

<template data-slot="title">Hello — My Site</template>

<h1>Hello, {{name}}!</h1>

Visit /hello?name=Joey — the handler fetches data, the template renders it.

7. Add a dynamic route

Create pages/posts/[slug].js:

const posts = {
  'hello-world': { title: 'Hello World', body: 'My first post.' }
}

export async function GET(req) {
  const post = posts[req.params.slug]
  if (!post) return new Response('', { status: 404 })
  return { post }
}

And pages/posts/[slug].phtml:

<h1>{{post.title}}</h1>
<p>{{post.body}}</p>

Visit /posts/hello-world. The slug parameter is available via req.params.slug.

8. Build a static site

For dynamic routes, export staticPaths() to tell the builder which URLs to generate:

// posts/[slug].js
const posts = {
  'hello-world': { title: 'Hello World', body: 'My first post.' }
}

export async function GET(req) {
  const post = posts[req.params.slug]
  if (!post) return new Response('', { status: 404 })
  return { post }
}

export async function staticPaths() {
  return Object.keys(posts).map(slug => ({ slug }))
}

Then run the build:

index97 build ./pages

Static assets from public/ are copied automatically. Output goes to dist/ by default:

index97 build ./pages --out ./output

Next steps

  • Architecture — understand the full routing system
  • Templates — all the template syntax
  • Layouts — named slots, defaults, layout data
  • Features — forms, partials, error pages, hot reload, CLI