• Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
stevekrouse

stevekrouse

reactRouter7MinimalExample

Full-stack example ReactRouter7 project
Remix of stevekrouse/reactRouter7Example
Public
Like
reactRouter7MinimalExample
Home
Code
5
backend
2
frontend
3
routes
3
shared
1
README.md
Environment variables
Branches
2
Pull requests
Remixes
History
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
README.md
Code
/
README.md
Search
…
Viewing readonly version of minimal branch: v29
View latest version
README.md

React Router 7 on Val Town — Minimal Example

The simplest possible full-stack SSR + SPA app on Val Town, built with React Router 7, Hono, and SQLite. A todo list in ~150 lines of app code that demonstrates every core pattern you need.

Want the full version? See the message board example with nested routes, search, multiple tables, and more.

What This Teaches

  1. SSR + Hydration — Server renders full HTML, client hydrates with hydrateRoot
  2. Shared route tree — Same routes.ts on server and client; serverLoader/serverAction keeps DB code off the client
  3. Single-fetch dataStrategy — One request per navigation via X-Data-Request header
  4. Loaders — Server-side data loading before render
  5. Actions — Form submissions via <Form method="post"> with intent pattern
  6. SQLite persistence — Val Town's project-scoped database
  7. Loading states — useNavigation() for optimistic UI

Project Structure

├── backend/
│   ├── index.ts          # Hono HTTP entry — SSR + data API + static files
│   └── database.ts       # Schema + all queries (one file, one table)
├── frontend/
│   ├── index.html        # HTML shell template
│   └── index.tsx         # Client hydration + dataStrategy
├── routes/
│   ├── Home.tsx          # The entire UI — todo list + add form
│   ├── Home.loader.ts    # Loads all todos from SQLite
│   └── Home.action.ts    # Handles "add" and "toggle" form submissions
├── shared/
│   └── routes.ts         # Route tree (shared between server + client)
└── README.md

How It Works

Request Lifecycle

First visit (SSR):
  Browser → GET / → Hono → createStaticHandler → runs Home.loader
    → renderToString → full HTML response → Browser hydrates

After hydration (SPA):
  User submits form → dataStrategy → POST / (X-Data-Request: true)
    → Hono → runs Home.action + Home.loader → JSON response
    → React Router updates UI

Key Pattern: serverLoader / serverAction

The route tree in shared/routes.ts is imported by both server and client. The helpers make this work:

  • On the server: dynamically import the real loader/action and call it
  • On the client: return a no-op stub (the dataStrategy handles fetching)

This means loader/action code and database dependencies never ship to the browser.

Key Pattern: dataStrategy (Single-Fetch)

Instead of React Router calling each loader individually on the client, a custom dataStrategy in frontend/index.tsx makes one HTTP request with an X-Data-Request: true header. The server runs all matched loaders/actions and returns their data as keyed JSON.

Extending This

Want to add...What to do
More pagesAdd routes to shared/routes.ts with new Component, loader, action
Nested layoutsUse children array in routes + <Outlet /> in parent component
More tablesAdd queries to backend/database.ts (or split into separate files)
SearchAdd a route with a loader that reads new URL(request.url).searchParams
Redirects from actionsReturn redirect("/path") — the dataStrategy handles it automatically

Getting Started

  1. Fork this val on Val Town
  2. The database auto-initializes on first request
  3. Visit the HTTP endpoint to see the app
  4. Start extending!

Tech Stack

LayerTechnology
ServerHono
RoutingReact Router 7
UIReact 18
StylingTwind (Tailwind via CDN)
DatabaseVal Town SQLite (std/sqlite@14-main)
RuntimeVal Town (Deno)
FeaturesVersion controlCode intelligenceCLIMCP
Use cases
TeamsAI agentsSlackGTM
DocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Open Source Pledge
Terms of usePrivacy policyAbuse contact
© 2026 Val Town, Inc.