This guide takes you from an empty checkout to a reactive JSX component running in the browser dev host — the same bundle that also runs on real Sony PSP hardware, PPSSPP, and headless Bun. If you want to explore PocketJS without installing anything, skip directly to the Playground and come back here when you’re ready for the local workflow.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/pocket-stack/pocketjs/llms.txt
Use this file to discover all available pages before exploring further.
Prerequisites
The JavaScript workflow needs one tool. The Rust toolchains are only required when you want to compile the core natively — you don’t need them to write UI or run the browser dev host.
The first
bun scripts/dev.ts run compiles the Rust core to wasm32 with cargo build. That takes a moment. Subsequent runs skip it and are fast.Clone and install
bun install pulls solid-js, the Vue Vapor packages, and all build-time tooling: the Babel + Tailwind-subset compiler, the font baker, and the dev host. There is no separate runtime to install — @pocketjs/framework is the package in this repo, exposed through subpath imports like @pocketjs/framework/components.Write your first component
Create What is happening here:
demos/hero/app.tsx. Layout is flexbox via View, text via Text, and styling via class — a build-time subset of Tailwind, not runtime CSS. State comes from the selected framework.- Solid
- Vue Vapor
Viewis the layout primitive — a flexbox node.Textis the text primitive. Both are imported from the framework’s/componentssubpath.focusableopts theViewinto d-pad focus traversal.onPressfires when that focused node is confirmed (Circle button on PSP, Enter or Space in the browser host).focus:bg-blue-500andactive:bg-blue-700are style variants baked into the binary style record. Focus changes swap style IDs natively — zero JS per transition.transition-colors duration-150declares a color tween. The tween ticks in Rust at a fixeddt = 1/60 s; JS only declares it.Show/ the conditional expression is standard framework control flow. PocketJS handles node insertion and removal on both sides of the FFI.Count: {count()}/Count: {count.value}is a mixed text run. The static string and the reactive value are laid out as one inline run by the text engine.
Write the mount entry
app.tsx exports a component but doesn’t put anything on screen. The mount entry does that. Put it in demos/hero/main.tsx:- Solid
- Vue Vapor
mount is imported from @pocketjs/framework (the package root). It:- Detects the current host (PSP / browser / headless Bun)
- Registers the generated style table
- Uploads images from the packed asset file on injected hosts (browser / Bun)
- Installs the per-frame host callback
mount(() => <App />).Build your app
One command transforms your JSX, compiles the styles it actually uses, bakes only the glyphs it actually renders, and bundles everything:This produces two files in
Vue Vapor builds use a
- Solid
- Vue Vapor
dist/:| File | What it is |
|---|---|
dist/hero.js | Your app bundled to a single IIFE (unminified) that any host loads |
dist/hero.pak | The packed asset file: compiled style table, baked font atlases, and images |
.vue-vapor suffix — dist/hero.vue-vapor.js and dist/hero.vue-vapor.pak.The build is two-pass:- Transform & collect. Babel runs over every module reachable from your entry (Solid’s universal preset + TypeScript), content-hash cached in
.cache/. As it goes it collects every class literal and every text codepoint from the AST. The Tailwind-subset compiler validates tokens and writesstyles.bin; the font baker rasterizes an Inter atlas containing only the characters your app uses; everything is packed intodist/hero.pak. - Bundle. Bun bundles the app (IIFE,
target: "browser", unminified) from the cached pass-1 transforms intodist/hero.js.
Run in the browser dev host
The dev host builds the Open http://127.0.0.1:8130/ in your browser. You’ll see a 480×272 canvas running the demo with virtual buttons for input.To target a specific demo or change the port:
wasm32 Rust core, builds the mounted demo, and serves it all in one command:There is no live reload. After editing a component, re-run
bun scripts/build.ts hero (or the full bun scripts/dev.ts) and reload the page manually. The first run compiles the Rust core with cargo build; subsequent runs skip that step and are much faster.(Optional) Ship to PSP hardware
With Rust nightly and This runs the same two-pass build, then calls To run on real hardware over PSPLINK:
cargo-psp installed, one command wraps your bundle into a PSP EBOOT:cargo psp with the correct environment for the mipsel-sony-psp target. The output is a EBOOT.PBP ready for ms0:/PSP/GAME/. For Vue Vapor:Framework selection
Framework selection is explicit. The default (frompocket.config.ts) is Solid:
--framework=vue-vapor to any of the scripts:
What’s next
- Architecture — how one Rust core drives all four hosts, and why the JS mirror tree never reads across FFI.
- Components —
View,Text,Image, and app-shell primitives likeFocusGrid,Modal, andPortal. - Styling — the full supported Tailwind utility set, variant syntax, and dynamic styling patterns.
- Animation — the
animate()andspring()APIs,transition-*classes, and fixed-dt animation tracks. - Input & focus — d-pad traversal,
onPress,FocusScope, and what happens when a focused node is removed.