Three of these run with no build step at all, and seven are applications with one.
cd examples/counter
node index.ts # or: bun index.tsNothing compiles them.
h is what JSX compiles to: <Row gap={1}/> and { component: 'Row', gap: 1 } are the same value.
So a file that calls h directly is a file the runtime already understands.
Node has stripped types by default since 23.6.
hello |
The smallest thing that runs |
counter |
useKeymap, useState and a timer you can pause - in h and in JSX |
chatunix |
A chat room over a socket or a port. Two processes, really talking |
Under bun, yes, and with no build.
counter/counter.tsx is the same program as counter/index.ts, in JSX.
Under node, no, and the reason is worth knowing because it is not a missing flag.
Node strips types; it does not transform syntax.
A .ts file with no non-erasable syntax is a file node runs by deleting the annotations.
JSX is not an annotation - <Box/> has to become a call - so node rejects .tsx outright:
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".tsx"
--experimental-transform-types does not help; it handles enums and namespaces.
So the trade is angle brackets against node, which is why the counter ships both and node index.ts is the default.
Bun compiles TSX, and the whole setup is one line beside the file:
{ "compilerOptions": { "jsxImportSource": "textui" } }textui rather than @textui/core, so one install is enough - the facade re-exports the JSX runtime for this.
Bigger programs, bundled by esbuild because they are many files rather than because textui needs it.
pnpm example todo # or arcade, surfaces, ink, flipbooktodo |
Screens, navigation, a store that persists |
arcade |
Frame loops, canvas painting, input timing |
surfaces |
An application with no shell, arranging its own chrome |
ink |
ColorText: a colour per cell, over a banner and over prose |
showcase |
The catalog on one screen, as a row that wraps. Writes its own screenshot |
flipbook |
An ASCII Motion document, played on its own timeline and edited in place |