place
v0.12 · DX overhaul — templates, logs, theme system

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, discoverPages } from '@place-ts/component/server'
import { pathRouter } from '@place-ts/routing'
import { mainLayout } from './layouts/main.layout'
import { tokens } from './theme'

export default await app({
  pages: await discoverPages('./src/pages'),
  layout: mainLayout,
  theme: tokens,
  router: pathRouter,
}).start()

One config. One .start() — env-aware: PLACE_BUILD=dist static-exports, anything else starts the server. The framework handles port discovery (auto-walks on EADDRINUSE), cap installation, island bundling, and pre-paint theme. No if (typeof window) branch in your code.

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.

Theme system, four-tier DX

new

<ThemeToggle /> drops in one tag for defaults; useTheme() is the headless primitive for BYO UI; setTheme(name) is the escape hatch. SSR ships no theme class when undecided — OS preference drives via @media, zero blip on hard refresh.

Scaffolder that respects you

new

`bunx @place-ts/create-app` — three curated templates (minimal · content · app) + five composable feature packs (theme, tests, CI, design, persistence). Interactive picker with sensible defaults; every choice exposed as a flag for CI.

Production deploy adapters

createFetchHandler() → Web-standard Request/Response. First-class adapters for Cloudflare Workers, Vercel Build Output, Deno Deploy. Static export emits a CSP-ready _headers file.

Server logs that scan

new

PLACE_LOG_LEVEL env var · scoped [hmr] / [isr] prefixes · terminal-rendered error frames with source-mapped file:line · static-asset noise suppressed at default level. Compact startup banner with Local + Network URLs.

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.

Motion as state

@place-ts/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.

One CLI, zero config

bun

Bun.serve + Bun.build out of the box. Tailwind v4 inline. Auto port-walk on EADDRINUSE. Content-hashed prod bundles. Source-map-aware dev overlay.

Nine systems, named

reactivitystate · watch · batch · resource · history · motion (spring · tween · sequence)
componentpage · layout · app · island · Tabs · Show · suspense · Form · virtualList · useTheme · setTheme · adapters
capabilitydefineCapability · scoped install · per-runtime impls
routingpathRouter · hashRouter · memoryRouter · route · searchParams
datacollection() — keyed CRUD over State<T[]> · trash/restore · cursor pagination
persistencepersistedState · localStorage · IndexedDB · cross-tab · server sync
searchsearchable() · reactive substring + token match · rank()-based ordering
securityCSP-strict · auto-CSRF · same-origin · body-limit · criticalAction() · macaroons · audit log
designButton · Field · Dialog · Sheet · Combobox · Toast · Tooltip · Menu · ThemeToggle · Prose · CodeBlock

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-ts/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