place

Why place

Three frameworks already own this space. Each made one structural mistake we're not making — and the difference shows up in code you write every day.

Honest framing
Next, Remix, and TanStack Start are all good. place isn't a strict upgrade — it's a different bet about what the framework should hide and what it should expose.

Hello, world

The smallest unit — declare a route. Same outcome, three philosophies.

ts
// src/pages/hello.page.tsx import { page } from '@place/component' export default page('/hello', { view: () => <h1>Hello</h1>, })

Next and Remix both encode the route in the file path. Move the file, your route moves with it; references to it (links, action callers) get stale. place puts the route in a value — refactor it, TypeScript flags every call site.

Server actions

Mutation is the API people get wrong first. The 'use server' marker hides too much; the FormData contract throws away types.

ts
export default page('/posts/:id', { on: { save: async (input: { title: string }, { params }) => { await db.posts.update(params.id, input) return { ok: true } }, }, view: () => /* call pageRef.save({...}) — fully typed */ null, })

place's action lives on the same page as its caller, with the full input type intact. The endpoint is visible (POST /posts/:id/_action/save); the path appears in your routes table; no Babel pass, no encrypted action IDs, no untyped FormData detour.

Capabilities, not context

React's context is global by default and silent on SSR mismatches. Capabilities are typed, scoped, and SSR-aware out of the box.

ts
import { defineCapability } from '@place/capability' export const NoteStoreCap = defineCapability<NoteStore>('NoteStore', { clientOnly: true, }) // In a page: const store = NoteStoreCap.use() // typed; provided once at app config

clientOnly: true auto-emits an SSR-safe placeholder when a browser-only cap is touched during render. No typeof window branches. No hydration mismatches.

Feature matrix

placeNext.js (App Router)RemixTanStack Start
Routeshow routes are declaredvaluesfile conventionfile conventionfile convention
Refactor by renameTS catches stale refspartial
Typed mutationscaller knows input typeFormData only
No codegen stepno .d.ts to regenerate
SSR-safe contextno typeof window checks
Streaming SSRsuspense boundaries
Built-in CSRFauto on every action
Built-in image optsharp + content hash
View Transitionsopt-in, zero JS
Bundle size (hello world)compressed client18 KB74 KB38 KB52 KB
Bundle numbers from a minimal "hello world" with the framework's default router only. Real apps add more; the relative gap stays.

When not to pick place

  • You already ship on Vercel's edge runtime and want native integration. place runs on Bun; adapters for other runtimes are in the roadmap, not shipped.
  • You have a large React codebase with deep React-specific patterns. place uses a different reactivity model; the migration cost is real.
  • You need an established ecosystem. place is v0.x; the recipe library will be 10× smaller than Next's for a while.