Taskboard 1. Local TSX project
Start the cumulative Taskboard as a TSX and ESM browser project. Every later stage extends the same TaskboardState, TaskboardApp and files.
Table of contents
Stage 1. Build a local TSX project
- Starting state
- Start with an empty local folder.
- Applied change
- Install Valyrian.js and the Node types, configure automatic TSX, generate the browser entry with inline and mount TaskboardApp on body with serializable TaskboardState data.
- Observable result
- The browser displays Taskboard with one task from the initial state.
- Next step
- Continue with Stage 2 and add interaction to this mounted application.
Understand the starting model
- Functional component
- TaskboardApp is a function that returns the view rendered by Valyrian.js.
- TSX
- TSX expresses the element tree passed to the Valyrian.js runtime. The configured jsxImportSource resolves that syntax through Valyrian.js.
- Props
- TaskboardApp receives its state object through props.
- Simple state
- createState returns the plain TaskboardState object used by this first render.
- inline and raw
- inline resolves an object with raw, map and file. build.mjs writes raw, the executable bundle, to public/client-entry.js.
- Mounting
- mount resolves "body", hydrates its existing DOM into a vnode tree, renders TaskboardApp and returns an empty string in the browser. The resulting DOM shows the Taskboard heading and "1 task".
tsconfig.json
json{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "valyrian.js",
"module": "ESNext",
"moduleResolution": "Bundler",
"skipLibCheck": true,
"strict": true,
"target": "ES2022",
"types": ["node"]
},
"include": ["src/**/*.ts", "src/**/*.tsx"]
}
build.mjs
jsimport fs from "node:fs";
import { inline } from "valyrian.js/node";
const client = await inline("./src/client-entry.tsx");
fs.mkdirSync("./public", { recursive: true });
fs.writeFileSync("./public/client-entry.js", client.raw);
fs.writeFileSync(
"./public/index.html",
'<!doctype html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Taskboard</title></head><body><script src="/client-entry.js"></script></body></html>',
);
src/state.ts
tsexport type Task = { id: number; title: string };
export type TaskboardState = {
tasks: Task[];
showDetails: boolean;
nextId: number;
loaded: boolean;
};
export function createState(
initial: Partial<TaskboardState> = {},
): TaskboardState {
return {
tasks: initial.tasks ?? [{ id: 1, title: "Learn Valyrian.js" }],
showDetails: initial.showDetails ?? false,
nextId: initial.nextId ?? 2,
loaded: initial.loaded ?? false,
};
}
src/app.tsx
tsximport type { TaskboardState } from "./state";
export function TaskboardApp({ state }: { state: TaskboardState }) {
return (
<main>
<h1>Taskboard</h1>
<p>{state.tasks.length} task</p>
</main>
);
}
src/client-entry.tsx
tsximport { mount } from "valyrian.js";
import { TaskboardApp } from "./app";
import { createState } from "./state";
const state = createState();
mount("body", <TaskboardApp state={state} />);
Run the project
Install Valyrian.js, build the TSX entry and serve the generated public directory.
bashnpm install valyrian.js@9.1.13 @types/node@22.15.3
node build.mjs
npx --yes serve@14.2.5 public --listen 8000Open Taskboard
Open http://localhost:8000. The page displays Taskboard with one task.
texthttp://localhost:8000