TSRX (TypeScript Render Extensions) is a way to write UI component code that stays readable and co-located. Structure, styling, and control flow live together, and the result stays fully backwards compatible with TypeScript and JSX.
1 export function Greeting({ name }: { name?: string }) @{
2 const message = name ? `Hello, ${name}` : 'Hello, stranger';
3
4 <>
5 <div class="card">
6 <p>{message}</p>
7 </div>
8
9 <style>
10 .card { padding: 1rem; }
11 </style>
12 </>
13 }You can think of TSRX as a spiritual successor to JSX — the same mental model of embedding UI directly inside TypeScript, but with its own flavor. Control flow, scoped styles, and statement containers sit in the template as first-class syntax instead of being squeezed through expression slots, and the language stays aware of them through to the compiled output.
TSRX is framework-agnostic and interoperable. Today it compiles to React, Preact,
Ripple, Solid, and Vue, with room for more targets over time. You can import.tsrxmodules from JS, TS, and TSX files and it just works.
Looking for the formal grammar and AST shape? Read thespecification →
Benefits of TSRX
When structure, control flow, styling, and component shape live in the same file, the source reads closer to the interface it describes. TSRX keeps that co-location legible with a structural rule: TypeScript setup stays first, then the scope resolves to one output node. That cuts down on ternaries, map chains, and render helpers while making refactors and onboarding less brittle.
1 function UserList({ users, showBio }: Props) @{
2 @for (const user of users) {
3 const initials = user.name.slice(0, 2).toUpperCase();
4 const role = user.admin ? 'Admin' : 'Member';
5
6 <div class="user-row">
7 <span class="avatar">{initials}</span>
8 <strong>{user.name}</strong>
9 <span class="badge">{role}</span>
10
11 @if (showBio && user.bio) {
12 const short_bio = user.bio.slice(0, 140);
13 const report = () => console.log('viewed ' + user.name);
14
15 <div class="bio-detail">
16 <p class="bio">{short_bio}</p>
17 <button onClick={report}>Viewed</button>
18 </div>
19 }
20 </div>
21 }
22 }Code is increasingly written, reviewed, and transformed with machine assistance. When UI structure is more explicit, editors, compilers, and code generation tools have a better surface to work with without changing the framework semantics underneath.
This is consistent with findings that language models attend unevenly to long contexts, performing best when relevant information sits close together rather than scattered across a window.
Ergonomics
TSRX takes care of awkward target-specific details where the compiler can preserve the runtime's semantics directly. The source stays readable for humans and predictable for language models, while target transforms handle the mechanical parts that are safe to automate.
Solid props can be destructured eagerly. The&{ ... }pattern compiles to lazy getters, so reads stay reactive without forcingprops.counteverywhere and without losing type inference on the destructured names.
1 function Counter(&{ count, label }: Props) @{
2 <section>
3 <h2>{label}</h2>
4 <p>Count: {count}</p>
5 </section>
6 }Locals can live next to the JSX that uses them. A statement container starts with TypeScript setup and ends with one output node, so derived values sit beside the markup they feed instead of piling up at the top of the component. Concerns stay together; readers (and LLMs) do less bookkeeping to follow the data flow.
1 function Cart({ items }: { items: Item[] }) @{
2 const subtotal = items.reduce((sum, item) => sum + item.price, 0);
3 const discount = subtotal > 100 ? 0.1 : 0;
4
5 <div>
6 <h2>Your cart</h2>
7 <p>Subtotal: ${subtotal}</p>
8 <p>Save: ${(subtotal * discount).toFixed(2)}</p>
9 </div>
10 }Tooling and adoption
TSRX ships with a language server for editor diagnostics, navigation, and completion. Prettier and ESLint plugins keep formatting and linting consistent inside existing repositories, so the language can be adopted seriously instead of treated like a compiler experiment. Plus support for many IDEs and editors including VS Code, Zed, Neovim, IntelliJ and Sublime.
It interoperates with existing TypeScript and TSX codebases rather than demanding a full rewrite. Teams can adopt it where ergonomics matter most while keeping the surrounding code familiar.
Smart compilation
The TSRX compiler parses component source into an AST and hands it off to
framework-specific plugins for code generation. Plugins such as@tsrx/react,@tsrx/preact,@tsrx/ripple,@tsrx/solid, and@tsrx/vuetransform the same AST into idiomatic output for their target runtimes, including scoped
CSS. New targets can be added as standalone compiler plugins without changing the
language itself.