A client-side task manager written in Rust and transpiled to SolidJS 2.0.
Unlike the comparison lab (which is a feature checklist), this is shaped like a
real app: a sidebar with views, a reactive task list, a progress panel that is
code-split, and a modal composer — all styled with Tailwind and fed by a
fetch of local seed data.
Nine components across nine files in src/:
| File | Component | Demonstrates |
|---|---|---|
src/main.rs |
App |
entry, create_store, create_signal, create_memo, two-closure create_effect, on_cleanup, reconcile, use_head, hash-based navigation, the hide done toggle, and the page layout (Errored / Show / Loading / Portal) |
src/sidebar.rs |
Sidebar, NavItem |
a context provider (<ThemeContext value=…>), component children, a computed class={…} for the active view, per-view count badges |
src/task_list.rs |
TaskList |
Show empty-state fallback, keyed For over the store |
src/task_item.rs |
TaskItem |
a match on priority, a reactive if-expression class, a namespaced class:done, a For over tags |
src/stats.rs |
StatsPanel (+ LazyStats) |
lazy! code-splitting, memos over the store, a namespaced style:width progress bar |
src/composer.rs |
Composer |
<Portal> modal, a ref-backed input (event handlers are zero-arg), priority buttons, calling a callback prop |
src/actions.rs |
add_task |
#[action] — a generator transaction that yields, pushing to the store |
src/api.rs |
Task, load_tasks |
a Loader const: an async fetch of /data/tasks.json |
src/theme.rs |
ThemeContext |
a module-level const holding a create_context, shared across files |
# from the repo root (build the CLI first)
cargo build -p solid-rs
cd examples/tasks
# emit JSX only (no node/npm needed) — prints the generated .jsx
../../target/debug/solid-rs check
# full build (needs Node.js >= 18):
../../target/debug/solid-rs install
../../target/debug/solid-rs build # or: solid-rs build
../../target/debug/solid-rs dev # build + serve on :3000, rebuild on changeOpen http://localhost:3000. The build also compiles css/styles.css with
Tailwind (@tailwindcss/cli) into dist/styles.css and copies data/ to
dist/data/ so the app's fetch("/data/tasks.json") resolves.
e2e/render.mjs asserts the initial DOM (tasks, sidebar, the lazy stats
panel) and e2e/reactivity.mjs toggles tasks, switches views, flips the
hide-completed toggle, and drives the Portal composer to add a task (jsdom, no
browser needed; the task fetch is mocked):
solid-rs install
solid-rs build
node e2e/render.mjs && node e2e/reactivity.mjs- Tailwind.
css/styles.cssstarts with@import "tailwindcss"and points@sourceat../src, because the class names live in Rust source (transpiled to JSX), not in a.jsx/.htmlfile Tailwind would scan by default. A couple of one-off styles (the.tickcheckbox, the.pill) are plain CSS utilities. - Event handlers are zero-arg. The transpiler only accepts
onclick=move || …(no event parameter). The composer therefore reads its input through aref: the signal setter is passed asref={set_input}, which Solid calls with the node on mount, and the submit handler readsinput().valueon demand. - No live text search. For the same reason there is no
oninput; the reactive filters are the active view and thehide donetoggle instead. ifinside afor-loop is avoided. Anifstatement is emitted asreturn cond ? … : undefined;, so inside a loop it ends the loop after the first item. The memos usetasks.filter(…).lengthand the toggle finds the row byfindIndexand mutates it in the draft, rather than looping.- Method names pass through to JS verbatim. Only JS-compatible methods are
used (
.filter,.findIndex,.push,.length,.replace); Rust-only ones (.is_empty,String::new,===) are not, and!=/==are the supported comparison operators. lazy!splitsStatsPanelinto its own chunk, loaded on demand and wrapped in a<Loading>boundary. The e2e proves the split is real by assertingmain.jsdoes not contain the stats markup.