Pyxle — an open-source, full-stack Python framework
Python & React.
One file.
A feature's Python and its React in one .pyxl file — server-rendered, hydrated, and no API layer to keep in sync.
MIT · v0.9.3 · Python 3.10+ · any ASGI host · ~2.2× faster SSR than Next.js →
# likes.pyxl
@server
async def load(req):
return {"likes": await db.count()}
@action
async def like(req):
return {"likes": await db.bump()}
export default function Likes({ data }) {
const like = useAction('like')
return <button onClick={() => like()}>♥ {data.likes}</button>
}
01 — The file
Eight files was never the point.
The usual React-plus-API stack scatters one feature across eight files, stitched together by convention and hope. In Pyxle it's one file — one read tells you everything it does.
The usual way
- app/api/likes/route.ts (replaced)
- lib/validators.ts (replaced)
- types/likes.d.ts (replaced)
- hooks/use-likes.ts (replaced)
- lib/api-client.ts (replaced)
- components/LikeButton.tsx (replaced)
- components/LikeButtonSkeleton.tsx (replaced)
- app/posts/[id]/page.tsx (replaced)
# likes.pyxl
@server
async def load(request):
pid = request.path_params["id"]
return {"id": pid, "likes": await db.likes(pid)}
@action
async def toggle_like(request):
body = await request.json()
return {"likes": await db.toggle(body["id"])}
import React from 'react';
import { useAction } from 'pyxle/client';
export default function Likes({ data }) {
const toggle = useAction('toggle_like');
return (
<button onClick={() => toggle({ id: data.id })}>
♥ {data.likes} likes
</button>
);
}
Change the Python. Watch the page.
The @server loader and @action handlers below execute in genuine, sandboxed CPython; the component renders live in the preview pane. Edit anything — nothing here is a video.
Edit the code → real Python boots.
03 — In the box
Everything you'd assemble, already assembled.
No meta-framework archaeology. Pyxle ships the parts a product actually needs, and stays out of the way for the rest.
04 — Studio & the debugger
Two breakpoints, one file.
Set one in the @server loader and one in the React component below it — both bind, in the same .pyxl. The VS Code extension contributes each half as its own F5 launch: Backend runs your dev server under the Python debugger, Frontend attaches the browser to it.
The same run serves Studio at /__pyxle/studio — every route with its loaders and actions, a tester that runs them against the live server, and the request feed as it happens. Dev-only by construction: a production pyxle serve doesn't contain it.
routes · tester · request feed · latency · resolved config · pyxle check
F5 needs the extension · already on Pyxle? pip install --upgrade pyxle-framework
Debug a .pyxl file →Studio, panel by panel →Get the VS Code extension ↗
# post.pyxl
@server
async def load(request):
pid = request.path_params["id"]
post = await db.post(pid)
return {"post": post}
export default function Post({ data }) {
const words = data.post.body.split(' ').length
return <h1>{data.post.title} · {words} words</h1>
}
05 — The numbers
Measured, not marketed.
Same hardware, same load generator, both frameworks tuned in good faith. When a setup is unfair to a competitor, we fix the setup — not the chart.
- faster server-side rendering than Next.js, per core
- ~2.2×
- smaller HTML than Next.js for the same page
- 2–3×
- FastAPI's throughput on database-heavy pages, at 8 workers
- 1.3×
- errors under sustained load
Measured July 2026 on Pyxle 0.7.1, and not re-measured for the 0.9 line. Reproduce them yourself — full methodology, hardware, and harness at /benchmarks →
Beyond raw speed — the full picture against Django, FastAPI, and Next.js is in the comparison →
$ pyxle check
ℹ️ Checked 2 .pyxl file(s) in my-app/
error: [python] line 2: expected ':'
--> pages/feed.pyxl
error: [jsx] line 6: JSX syntax error: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...</>? (6:8)
--> pages/likes.pyxl
❌ Check failed with 2 error(s)
$ echo $?
106 — For the agents
One file is also how an agent reads.
Context windows are budgets. A Pyxle feature is one read — loader, mutations, and UI in a single pass, not a scavenger hunt across eight files before the first edit.
And it's all Python — the language models write best.
07 — Markdown, built in
Every page is also Markdown.
Your readers increasingly arrive with an AI in the loop — and it wants text, not your HTML. Flip one flag and every page serves a clean Markdown twin: append .md to any URL, or send Accept: text/markdown. This page included.
$ curl pyxle.dev/docs/core-concepts/routing.md
> Markdown rendition of that page,
> served for agents. Index: /llms.txt
# Routing
Pyxle uses **file-based routing**. The
structure of your `pages/` directory is
your URL routes — no router config,
no tag soup, just the words.08 — In the wild
Built with Pyxle, running live.
One reference app that uses every feature, plus a few small specimens — each a handful of .pyxl files, deployed the boring way. Open the source next to the live app; the whole feature really is in one file.
The reference app — a service-status page with a live incident war room — built to exercise every Pyxle feature in one coherent product. Python loaders and React UI in the same .pyxl file, server-rendered, streamed, and hydrated.
Smaller specimens
09 — Start
Four commands. The fourth is a running app.
pip install pyxle-frameworkinstalls the compiler, server, and CLI
pyxle init my-app && cd my-appscaffolds pages/, config, and a first route
pyxle installinstalls the pinned JS toolchain
pyxle devSSR dev server with HMR at localhost:8000
Then open pages/, add a file, and you've shipped a route. The docs take it from there.
This form is the demo.
The input below posts to an @action defined in index.pyxl — the file that rendered this page. Release notes, occasionally. No noise.
def subscribe_newsletter(request) · validated, rate-limited, and stored by an @action on this server.