place
v0.11 · static export + first public deploy

One platform.
Nine systems.
Visible magic.

place is a TypeScript-first web platform built on Bun. Smaller surface than Next, fewer footguns than Remix, more honest than TanStack. Nine composable systems with explicit boundaries — reactivity, component, capability, routing, data, persistence, search, security, design.

The shape

src/app.ts
src/app.tsts
import { app } from '@place/component'
import { pathRouter } from '@place/routing'
import home from './pages/home.page'
import about from './pages/about.page'
import { rootLayout } from './layouts/root.layout'

export default app({
  pages: [home, about],
  layout: rootLayout,
  router: pathRouter,
}).run()

One config. One .run(). The framework handles server/client dispatch, port discovery, cap installation, and bundling. No if (typeof window) branch.

What you get

Routes as values

Every page is a value. Move a file, route doesn't break. No codegen, no stale .d.ts, no file-system magic. Refactors are TypeScript renames.

Capabilities, not context

Typed slots installed with explicit scope. No useContext action-at-a-distance. SSR-safe by construction — clientOnly caps auto-emit placeholders.

Reactivity, not re-render

tc39

Fine-grained signals + two-color graph coloring. The same algorithm TC39 standardizes. No virtual DOM, no per-tick reconciliation.

Real SSR

streaming

suspense() with comment-marker swap. ISR via lazy stale-while-revalidate. Per-route security headers. Auto-CSRF + same-origin + body-limit defaults.

Actions, typed

Co-located on:{} dict per page. Auto-typed callers; the path is visible; no Babel pass, no encrypted action IDs. Schema-agnostic — bring your own validator.

Motion as state

new

@place/reactivity/motion — animate() returns a Derived<number>. Springs, tweens, sequences. SSR resolves to rest. No <motion.div> factory, no two-runtime split, no 34KB floor.

Design system, native-first

new

@place/design — Button, Field, Dialog, Toast, Tooltip, Menu, Avatar, Badge, Card. Built on <dialog>, the Popover API, :user-invalid. Curated package, not a 10th system.

Strict-CSP by default

No inline scripts, no inline styles (style:* directives use setProperty). Content-Security-Policy ships sane defaults; CSRF, same-origin, body-limit, prototype-pollution guards all on.

One CLI, zero config

bun

Bun.serve + Bun.build out of the box. Tailwind v4 inline. View Transitions opt-in. Content-hashed prod bundles. Source-map-aware dev overlay.

Nine systems, named

reactivitystate · watch · batch · resource · history
componentpage · layout · app · island · Tabs · Show · suspense · Form · virtualList
capabilitydefineCapability · scoped install · per-runtime impls
routingpathRouter · hashRouter · memoryRouter · route · searchParams
datacollection() — keyed CRUD over State<T[]>
persistencepersistedState · localStorage · IndexedDB · cross-tab · server sync
searchsearchable() · reactive substring + token match
securityCSP-strict · auto-CSRF · same-origin · body-limit
designtheme() · themeTokens() · typed CSS-variable theming

Each system has its own charter, its own ADRs, and its own deliberately not doing list. Use what helps. Drop what doesn't.

Built on the platform itself.

This docs site is a place app. The interactive reactivity demo on the reactivity page uses the same @place/reactivity primitives the framework ships. The Cmd+K search palette uses the same globalKey + state you'd use in your app. There's no privileged internal surface you can't reach.

See more examples