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

stevekrouse

vtrr

Val Town React Router fullstack framework
Public
Like
1
vtrr
Home
Code
8
demos
3
README.md
client-runtime.ts
jsx-runtime
mod.ts
routes.ts
server.ts
types.ts
Environment variables
Branches
1
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
/
demos
/
message-board
/
README.md
Code
/
demos
/
message-board
/
README.md
Search
…
Viewing readonly version of main branch: v87
View latest version
README.md

Message Board Demo

A fullstack community message board built with the vtrr framework — demonstrating nested routes, server loaders/actions, SQLite persistence, search, and client-side navigation.

▶ Live Demo

Features

  • Topics — browse, create, and view discussion threads
  • Messages — post messages in topics with optimistic form resets
  • Search — full-text search across topics and messages with deep-link to individual messages
  • SSR + Hydration — pages render on the server, then hydrate for instant client-side transitions
  • SQLite persistence — data lives in Val Town's built-in SQLite, seeded with sample data on first run

Route Map

Rendering mermaid diagram...

🟦 Routes   🟩 Loaders (read data)   🟪 Actions (write data)

Project Structure

demos/message-board/
├── index.tsx                  # Entry point — route definitions + createApp()
├── types.ts                   # Shared types (Topic, Message, SearchResults)
│
├── database/
│   ├── migrations.ts          # Schema creation + seed data
│   └── queries.ts             # All SQL queries (CRUD + search)
│
├── components/
│   ├── LoadingSpinner.tsx      # Reusable loading indicator
│   ├── MessageForm.tsx         # Post a new message (uses useFetcher)
│   ├── MessageList.tsx         # Render messages with highlight support
│   └── SearchForm.tsx          # Search input with navigation
│
└── routes/
    ├── App.tsx                 # Root layout — <Outlet>, loading state, footer
    ├── Home.tsx                # Topic list + "Create Topic" form
    ├── Home.loader.ts          # Loads all topics from DB
    ├── Topic.tsx               # Topic detail view + message list + post form
    ├── Topic.loader.ts         # Loads topic + its messages
    ├── Topic.action.ts         # Creates a new message in a topic
    ├── TopicMessage.loader.ts  # Same as Topic.loader (with message highlight)
    ├── Topics.action.ts        # Creates a new topic → redirects to it
    ├── Search.tsx              # Search results (topics + messages)
    └── Search.loader.ts        # Runs search query against DB

Data Flow

Rendering mermaid diagram...

Database Schema

Rendering mermaid diagram...

The database is auto-initialized on cold start via initializeDatabase() and seeded with 3 sample topics and 5 messages if empty.

How It Uses vtrr

This demo showcases the key patterns of the vtrr framework:

1. Route definitions with string-based loaders/actions

Create val
export const routes = defineRoutes([ { path: "/", Component: App, action: "./routes/Topics.action.ts", // server-only, never shipped to client children: [ { index: true, Component: Home, loader: "./routes/Home.loader.ts" }, { path: "topics/:topicId", Component: Topic, loader: "...", action: "..." }, ], }, ]);

Loaders and actions are specified as relative file paths — the server dynamically imports them, while the client only gets lightweight stubs.

2. Loader pattern — export a named loader function

Create val
// Home.loader.ts export async function loader() { return { topics: await getAllTopics() }; }

3. Action pattern — form mutations with redirects

Create val
// Topics.action.ts export async function action({ request }) { const formData = await request.formData(); const topic = await createTopic(formData.get("title")); return redirect(`/topics/${topic.id}`); }

4. createApp with custom <head> content

Create val
export default createApp({ routes, head: ` <title>Community Message Board</title> <script src="https://cdn.twind.style" crossorigin></script> `, });

Styling

Uses Twind (Tailwind-in-JS) loaded from CDN — zero build step, just write Tailwind classes directly in JSX.

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.