Quick Start
Create a new PraxisJS project and build your first component in minutes.
Quick Start
Automatic setup (recommended)
create-praxisjs scaffolds a project with TypeScript, Vite, JSX, and HMR already configured. It's the fastest way to get started.
npm create praxisjs@latestThe CLI asks, in order:
- Project name
- Template
- Desktop app with Tauri v2? (see below)
- Add Kosmesis UI components? (see below)
- Add an AI integration? (see AI & MCP)
| Template | What's included |
|---|---|
| Minimal | core, decorators, jsx, runtime |
| With Router | Minimal + router |
| Full | Router + store, di, composables, concurrent, devtools |
| Blog | Router + content: a markdown blog with frontmatter and reactive collections |
Start the dev server:
cd my-app
npm install
npm run devFlags answer a question ahead of time, in any position after the project name. With npm, put them after -- (npm create praxisjs@latest my-app -- --tauri --kosmesis) so npm passes them to the CLI instead of reading them as its own:
| Flag | Answers |
|---|---|
--tauri / --no-tauri | Desktop app with Tauri v2 |
--kosmesis | Kosmesis with Tailwind CSS |
--kosmesis=praxisjs-css (or --kosmesis praxisjs-css) | Kosmesis with @praxisjs/css (--kosmesis=tailwind works too) |
--no-kosmesis | No Kosmesis |
Desktop app with Tauri v2
After the template, the CLI asks Desktop app with Tauri v2? Answer yes, or pass --tauri to skip the question (--no-tauri skips it too, with no desktop app). The option works with every template:
npm create praxisjs@latest my-app -- --taurinpm needs the -- so that it passes --tauri to the CLI instead of reading it as one of its own options.
The CLI runs the official Tauri CLI (tauri init, through npx, pnpm dlx, yarn dlx or bunx) to generate src-tauri/, then adapts the project to it:
| File | Change |
|---|---|
src-tauri/tauri.conf.json | devUrl http://localhost:5173, frontendDist ../dist, beforeDevCommand / beforeBuildCommand with your package manager, product name and window title from the project name, version read from package.json, identifier com.example.<name> |
src-tauri/src/lib.rs | A greet command, registered with invoke_handler |
package.json | tauri, tauri:dev and tauri:build scripts, @tauri-apps/api and @tauri-apps/cli (latest 2.x) |
vite.config.ts | Fixed port 5173 with strictPort, clearScreen: false, TAURI_ENV_* env prefix, HMR host for physical iOS/Android devices (TAURI_DEV_HOST), src-tauri left out of the watcher, webview build targets for tauri build |
src/greet.tsx (minimal) or src/components/greet.tsx | A component that calls the Rust command with invoke("greet", { name }), rendered by src/app.tsx |
.gitignore, README.md | Tauri build output ignored, a short "Desktop app" section |
cd my-app
npm install
npm run tauri:dev # Vite + native window, with hot reload
npm run tauri:build # installers in src-tauri/target/release/bundle/npm run dev still serves the plain web app. In a browser the greet component explains that it needs the desktop window, because isTauri() is false there.
Building the desktop app needs Rust and the system dependencies of your platform. The CLI checks for cargo and, when it is missing, prints what to install (or how to load it into PATH when rustup is installed but the terminal predates it). Generating the project does not need Rust.
Replace identifier in src-tauri/tauri.conf.json with your own reverse domain before you ship.
If the Tauri CLI cannot be downloaded or fails, the CLI says why, removes the partial src-tauri/ and leaves the web project as it would be without --tauri. Run npx @tauri-apps/cli@2 init in the project later and follow the Tauri Vite guide.
UI components with Kosmesis
Kosmesis is the copy-paste component collection for PraxisJS, built on the Morphos primitives. At Add Kosmesis UI components?, pick a styling system, or pass --kosmesis (Tailwind CSS) or --kosmesis=praxisjs-css:
| Choice | Components are styled with |
|---|---|
| Yes, with Tailwind CSS | Tailwind CSS v4 utility classes and class-variance-authority |
| Yes, with @praxisjs/css | Typed CSS-in-TS with @praxisjs/css, no Tailwind |
npm create praxisjs@latest my-app -- --kosmesisThe CLI runs kosmesis init without prompts through your package manager (npx, pnpm dlx, yarn dlx or bunx, with kosmesis@^0.6.0), then kosmesis add button card. That installs the project's dependencies, so the project is ready to run when the CLI ends. Kosmesis:
- writes
components.jsonand the theme tokens:src/style.csswith Tailwind (the template's own styles stay below them),src/lib/kosmesis-theme.tswith@praxisjs/css; - adds the
@tailwindcss/viteorpraxisjsCSS()plugin and the@/*→src/*alias tovite.config.tsandtsconfig.json; - wires
@IconProvider(LucideSource)(and@Themed(...)with@praxisjs/css) on theAppcomponent insrc/app.tsx; - copies the starter components into
src/components/ui/.
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";Add more components with npx kosmesis add <component>: see the Kosmesis components.
Kosmesis works with every template and with --tauri: the components render in the desktop window as in the browser, and the Tauri vite.config.ts gets the same plugin and alias. If Kosmesis cannot be set up (no network, or the kosmesis release is not reachable), the CLI says why, leaves the project as it would be without --kosmesis and prints the commands to run in the project later.
Existing project
praxisjs add kosmesis sets Kosmesis up in a project you already have, Tauri apps included. It checks that the project depends on @praxisjs/core, installs its dependencies when node_modules is missing, runs kosmesis init and says what to do next:
npx praxisjs add kosmesisIn a terminal, without flags, kosmesis init asks its own questions. With flags it runs without prompts: npx praxisjs add kosmesis --style-system praxisjs-css --components button,card. See the CLI reference for every flag.
Manual setup
Prefer to wire things up yourself? Here's what you need.
1. Install packages
npm install @praxisjs/core @praxisjs/jsx @praxisjs/runtime @praxisjs/decorators
npm install -D @praxisjs/vite-plugin2. Configure Vite
// vite.config.ts
import { defineConfig } from 'vite'
import { praxisjs } from '@praxisjs/vite-plugin'
export default defineConfig({
plugins: [praxisjs({ hmr: true })],
})3. Configure TypeScript
// tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"jsxImportSource": "@praxisjs/jsx",
"useDefineForClassFields": true,
"strict": true,
"noEmit": true
}
}Your first component
// src/Counter.tsx
import { StatefulComponent } from '@praxisjs/core'
import { Component, State, Prop } from '@praxisjs/decorators'
@Component()
class Counter extends StatefulComponent {
@Prop() initialCount = 0
@State() count = 0
onMount() {
this.count = this.initialCount
}
render() {
return (
<div>
<p>Count: {() => this.count}</p>
<button onClick={() => this.count++}>+</button>
<button onClick={() => this.count--}>−</button>
</div>
)
}
}Why {() => this.count} instead of {this.count}?
render() runs once on mount. Without the arrow function, this.count is read once at that moment and never updates. Wrapping it in {() => this.count} creates a live subscription: when count changes, only that specific <p> node updates.
Mount the app
// src/main.ts
import { render } from '@praxisjs/runtime'
import { Counter } from './Counter'
render(() => <Counter initialCount={5} />, document.getElementById('app')!)AI & MCP
@praxisjs/mcp gives any MCP-compatible AI assistant live access to praxisjs.org documentation, your project's structure and its project memory. Two AI integrations install the PraxisJS skills and configure the server:
| Integration | AI assistant | Project memory file | Skills | MCP server config |
|---|---|---|---|---|
| Claude Code | Claude Code | CLAUDE.md | .claude/skills/praxisjs*/ | .mcp.json, approved in .claude/settings.json |
| Codex | OpenAI Codex | AGENTS.md | .agents/skills/praxisjs*/ | .codex/config.toml |
New project
At the "Add an AI integration?" step of create-praxisjs, pick Claude Code or Codex. The CLI ends with a setup prompt to paste into the agent.
Existing project
Run the ai add command in the project root:
npx praxisjs ai addIt ends with a prompt to map the project into the memory. To uninstall an integration, run praxisjs ai remove: see the AI skills guide for what it does and doesn't delete.
MCP server only
To add only the MCP server (without the skills):
npm install -D @praxisjs/mcppnpm add -D @praxisjs/mcpyarn add -D @praxisjs/mcpbun add -d @praxisjs/mcpSee the MCP Server page for client-specific setup (Cursor, VS Code/Copilot, Windsurf, Codex).
Set up with your agent
The CLIs print these prompts when they install the AI integration; copy them from here too. Restart the agent first (Claude Code: approve the praxisjs server when it asks, check with /mcp).
New project. create-praxisjs ends with it when you picked an integration:
Set up this new PraxisJS project with me. Check that the praxisjs MCP server is connected, then read the praxisjs skill and the docs it points to. Run its session start checklist: ask me the setup questions in one message, set up the project memory, and confirm the folder structure and conventions with me before writing any code.Existing project. praxisjs ai add, and praxisjs ai sync when it adds the MCP server, end with it. So does praxisjs add kosmesis when an AI integration is installed, so the new components get recorded:
Map this PraxisJS project into its memory. Check that the praxisjs MCP server is connected, then run the praxisjs-memory-bootstrap skill over the whole project. Confirm the folder structure and conventions you find with me, and ask about anything the code does not make clear.The setup prompt runs the skill's first session: your preferences, then whether the project is still the empty scaffold (memory set up without an analysis) or should be mapped. The map prompt runs the praxisjs-memory-bootstrap skill directly, which analyzes the project in focused passes and writes .praxis/ through the MCP server. It is safe to run again later: entries are updated, not duplicated. See Project memory.
What's next?
- Components — understand StatefulComponent vs StatelessComponent
- Reactivity & Signals — how signals and arrow functions work under the hood
- JSX Syntax — every JSX pattern: conditionals, lists, events, refs
Introduction
What PraxisJS is, why it exists, and how it approaches frontend reactivity.
AI Skills
How to use PraxisJS AI skills — docs-first development through the MCP server, no duplicated code, the project's folder structure, a versioned project memory and one-time project config for Claude Code and Codex.