The road to a fully-typed Pyxle
Status: roadmap. This page describes where Pyxle's type story is going, not a feature that ships today. For what's available now — typed editor support, generated declarations, and the
pyxle typecheckgate — see the TypeScript guide. Nothing here changes the fact that JavaScript is fully first-class today: every npm package works in a.pyxlJSX block, and nothing is blocked.
Pyxle is Python-first. That is a deliberate choice, not a limitation — but it raises an obvious question for anyone who has felt the pull of end-to-end types in a TypeScript stack: if my server is Python and my client is React, where do my types come from?
This is our answer, and the direction we are building toward.
The vision: one source of truth, typed across the boundary
In a Pyxle app the shape of your data is defined once, in Python — a loader
returns a Pydantic model, a dataclass, a TypedDict, or an annotated dict; an
@action accepts a validated body. The endgame is that this shape flows to the
client as a real type, so the data your component receives is typed from the
Python that produced it, and a mismatch is caught before the page ever renders.
@server loader returns User ──▶ component receives data: User
@action accepts CreatePost ──▶ useAction is typed to CreatePostNo hand-written client interfaces to keep in sync. No duplicated shapes drifting apart. Python stays the single source of truth, and the type crosses the Python↔React boundary with it. That is the thing a Python-first full-stack framework is uniquely positioned to offer — and it's what "a fully-typed Pyxle" means.
What's already true today
This isn't a green field. The framework already understands the boundary the type bridge will cross:
pyxle checkcatches undefined names and semantic mistakes in your.pyxlfiles before they run.pyxle typecheckgenerates a full set of.d.tsdeclarations for thepyxle/clientruntime and type-checks your client against them withtsc— so the framework surface is already typed in your editor and gateable in CI.@actionbodies validate against Pydantic models at runtime today, andpyxle openapiemits an OpenAPI 3.1 schema straight from those models. The server already treats your Python types as the contract.- The compiler extracts loader and action metadata at build time. The information the type bridge needs to reason about is already on the table.
So the direction below isn't a rewrite — it's connecting foundations that are already in place.
Where it's going
Two pieces, in roughly this order:
Author your client in TypeScript. Write the JSX half of a
.pyxlfile in TSX — annotations,interface, generics — with full editor intelligence. (Today the client block is plain JSX that gets type-checked but not type-annotated; this lifts that restriction.)Forward Python types to the client. Derive a TypeScript type from a loader's or action's Python return/parameter type and thread it into the component's props, kept in sync automatically as your Python changes. This is the differentiator, and the harder half — it's where most of the design work lives.
We're describing the direction, not committing to a specific mechanism yet. The right inference strategy, code-generation approach, and editor integration are exactly what a dedicated design pass will settle — and we'd rather ship that properly than nail a half-answer to a page.
Principles we're holding to
- Python is the source of truth. Types are derived from your Python, never hand-duplicated on the client.
- TypeScript is opt-in, not a tax. JavaScript stays fully first-class. A JS Pyxle app is a real, supported Pyxle app — today and after this lands.
- No lock-in and no magic. Generated types are inspectable; nothing hides behavior — in keeping with Pyxle's "no magic" design principle.
- Honest tooling. A green type-check will mean what it says.
A taste of what it should feel like
Illustrative — this is the target ergonomics, not current syntax.
# posts/[slug].pyxl
class Post(BaseModel):
slug: str
title: str
body: str
@server
async def load(request) -> Post: # Python defines the shape, once
return await db.get_post(request.path_params["slug"])// same file, client half — the type comes from the loader, for free
export default function PostPage({ data }: { data: Post }) {
// ^^^^ derived from the Python above
return <article><h1>{data.title}</h1>{/* data.tital → caught before render */}</article>
}The point isn't the syntax — it's that Post was written once, in Python,
and the client got it for free.
Status and how to follow along
This is planned, not scheduled — we're deliberately not attaching a version or date to it, because the design pass comes first and we won't pre-commit to a mechanism we haven't validated. What you can rely on in the meantime:
- JavaScript is fully supported, with no capability or package gaps.
- The typed-editor-support and
pyxle typecheckfeatures on the TypeScript guide are here today. - When the type bridge lands, it will be additive — it won't break your JS app.
If typed end-to-end data is the thing that would make Pyxle a yes for you, this page is our commitment that it's where we're headed — and that we intend to do it right.