The Format
One File. Full Stack.
Python server logic and React UI live in the same .pyxl file. The compiler splits them apart. You never think about it.
from pyxle import __version__
@server
async def load_dashboard(request):
user = await get_current_user(request)
stats = await fetch_stats(user.id)
return {"user": user.name, "stats": stats}
@action
async def update_settings(request):
body = await request.json()
await save_settings(request.state.user_id, body)
return {"ok": True, "message": "Settings saved"}
import React from 'react';
import { Head, useAction } from 'pyxle/client';
export default function Dashboard({ data }) {
const save = useAction('update_settings');
return (
<>
<Head>
<title>My Dashboard</title>
</Head>
<h1>Welcome, {data.user}</h1>
<StatsGrid stats={data.stats} />
<SettingsForm onSave={save} />
</>
);
}
@serverAsync Python function that fetches data. Receives a Starlette Request, returns a dict. It becomes React props.
@actionServer mutation callable from the browser. useAction() sends a POST, Python processes it, result comes back as JSON.
export defaultStandard React component. Receives { data } from the loader. Server-rendered, then hydrated on the client.
<Head>Controls the document head. Drop it anywhere in your component tree — title, meta, link, script. Dynamic values use normal JSX interpolation.
Live Demo
Server Data
The @server loader runs on every request. Python fetches data, React renders it. Refresh to see it change.
@server
async def load_playground(request):
views = increment_playground_views()
reactions = get_reactions()
return {
"serverTime": datetime.now().isoformat(),
"pythonVersion": platform.python_version(),
"requestId": uuid.uuid4().hex[:8],
"renderMs": render_ms,
"totalViews": views,
"reactions": reactions,
}
Live Result
This data came from Python, rendered by React, in one file.
Live Demo
Server Actions
Click a reaction. It hits Python, writes to SQLite, and returns. Every count is real and persistent.
React to this page
0 totalEvery click is a POST to Python. No API routes needed.
@action
async def react_emoji(request):
body = await request.json()
emoji = body.get("emoji", "")
if emoji not in VALID_EMOJIS:
raise ActionError("Invalid reaction")
new_count = increment_reaction(emoji)
return {"ok": True, "emoji": emoji, "count": new_count}
import React, { useState } from 'react';
import { useAction } from 'pyxle/client';
export default function Reactions({ data }) {
const react = useAction('react_emoji');
const [counts, setCounts] = useState(data.reactions);
async function handleClick(emoji) {
const result = await react({ emoji });
if (result.ok) {
setCounts(prev => ({ ...prev, [result.emoji]: result.count }));
}
}
return (
<div>{/* emoji buttons + counts */}</div>
);
}
Live Demo
Multiple Actions, One File
A second @action in the same file. Type text, pick a transform — Python processes it on the server.
@action
async def transform_text(request):
body = await request.json()
text = (body.get("text", "") or "")[:500]
mode = body.get("mode", "upper")
transforms = {
"upper": str.upper,
"lower": str.lower,
"title": str.title,
"reverse": lambda t: t[::-1],
"word_count": lambda t: str(len(t.split())) + " words",
"char_count": lambda t: str(len(t)) + " characters",
}
return {"ok": True, "result": transforms[mode](text)}
Navigation
Instant Page Transitions
Click any link below. No reload, no white flash. Pyxle fetches only the data and swaps React props.
View page source (Ctrl+U) — the HTML is already there. Server-rendered by Python.
Routing
Files Are Routes
Drop a .pyxl file in pages/. It's a route. Dynamic segments, catch-all routes, layouts — zero configuration.
Features
And There's More
Everything you need to build production-ready full-stack apps.
SSR + Hydration
Every page server-rendered, then hydrated. Fast first paint, great SEO.
CSRF Protection
Enabled by default. Double-submit cookie pattern. Zero config.
Error Boundaries
error.pyxl files catch errors at any directory level.
Nested Layouts
Compose UIs with layout.pyxl. Each level wraps the level below.
Head Management
Dynamic <title>, meta tags via HEAD variable or <Head> component.
Middleware
Application-level and route-level hooks. Full Starlette integration.
API Routes
Pure Python endpoints under pages/api/. Any HTTP method.
Env Variables
PYXLE_PUBLIC_ prefix for safe client injection. Server secrets stay secret.
TypeScript
JSX type checking via Vite. Full Python-to-React type bridge on the roadmap.
Tailwind CSS
Pre-configured with dark mode. Built-in watcher in dev.
Client Navigation
<Link> for instant SPA-style routing. Hover prefetch built in.
Hot Reload
Python + JSX changes reflect instantly. Vite HMR built in.
Ecosystem
The Foundation Is Solid
The core framework is stable and battle-tested. These tools are in active development to grow the ecosystem.
Pyxle Auth
In developmentDrop-in authentication and session management. OAuth providers, email/password, magic links — all pre-wired.
Pyxle DB
In developmentDatabase toolkit with migrations, query builder, and connection pooling. SQLite to Postgres — same API.
Pyxle Langkit
BetaVS Code extension with .pyxl syntax highlighting, completions, hover, go-to-definition, diagnostics, and formatting.
Install from VS Code MarketplaceDebug Tools
In developmentBuilt-in error overlay, request inspector, and performance profiling. See exactly what your server does.
Ready to Build?
Get started in under a minute.