"After playing around with Replicant, I realized I could build Web Components without React — actually, even without Replicant.
I'm wondering if it's called 'Replicant' because of Blade Runner (I love Blade Runner).
Maybe I could name my own library something similar... Tyrell? No, that feels pretentious.
Anyway, I don't really want to type something long like
tyrell-button. It should be shorter — maybety-button.Yes! Let's call it ty."
Nine months later…
"Nine months in, ty still reads as 'thank you' — Just had to cut that and call it properly."
"The library is Tyrell. CLJS namespaces are
tyrell.*now (tyrell.router,tyrell.components,tyrell.lucide). Component tags stayty-*— web custom elements need the dash anyway, and two letters earn their keep. CSS classes stayty-*for the same reason. The prefix earned the shortcut; the brand didn't.""No thanks involved. Just replicants."
More framework than framework. Tyrell ships Web Components that work everywhere — React, Vue, HTMX, vanilla JS, ClojureScript.
Tyrell is a framework-agnostic evolution of Toddler, a ClojureScript UI library built on Helix (React).
Toddler provided great components (calendar, dropdown, routing, icons) but was locked to React/Helix. Tyrell takes that component library and rebuilds it with Web Components, making the same functionality available everywhere — React, Vue, HTMX, vanilla JS, and all ClojureScript frameworks (Replicant, UIx, Reagent).
Same components. Zero framework lock-in.
Always loads the latest version:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tyrell-components/css/tyrell.css">
<script src="https://cdn.jsdelivr.net/npm/tyrell-components/dist/tyrell.js"></script>Pin to a specific version (recommended for production):
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tyrell-components@1.0.0-RC6/css/tyrell.css">
<script src="https://cdn.jsdelivr.net/npm/tyrell-components@1.0.0-RC6/dist/tyrell.js"></script>Browse all available versions on NPM or jsdelivr.
Then use components anywhere:
<ty-button flavor="primary">Click me</ty-button>
<ty-dropdown label="Country" placeholder="Select...">
<ty-option value="us">United States</ty-option>
<ty-option value="de">Germany</ty-option>
</ty-dropdown>Tyrell is documented for AI agents from day one. Two artifacts make it work:
- Agent instructions snippet — drop into your project's
CLAUDE.md,.cursorrules,.windsurfrules,.github/copilot-instructions.md, or whichever instruction file your agent reads. The snippet teaches the agent the rules (colors come from Tyrell, events live onevent.detail, icons need registration, etc.) and points at the doc index. llms.txt— machine-readable index of every guide, served as raw markdown. Tell your agent to fetch it once and it has a navigable map of the entire documentation set. Per the llmstxt.org convention.
For agents with web fetch (Claude Code's WebFetch, Cursor's @web, ChatGPT browsing), pointing them at https://gersak.github.io/tyrell/llms.txt at session start is enough. For agents without it, paste the snippet into the instruction file and the agent learns the rules without any network calls.
The treasure-map entry point for AI navigation is guides/AI_GUIDE.md — two routing tables that get an agent to the right framework guide in one hop.
Pick the entry point for your stack:
- Vanilla JS Guide — bundlers, subpath imports, icon tree-shaking, code splitting, SSR
- React Guide —
tyrell-reactwrappers - Vue Guide — Vue 3 / Nuxt native usage (no wrapper package)
- Svelte Guide — Svelte 5 / SvelteKit native usage (no wrapper package)
- Quickstart — five-minute setup for any CLJS view layer
- CLJS Substrate Reference — distribution, shadow-cljs, raw interop, icon tree-shaking
- UIx Guide —
defui+$, hooks-only, auto-camelCase props - Reagent Guide — hiccup +
r/atom,:>interop with camelCase props - Helix Guide —
defnc+$,helix.dom/hooks, JSX-feeling - Replicant Guide — non-React, raw
<ty-*>elements - Component Guide —
tyrell.shimfor building Web Components in CLJS - Code Splitting — shadow-cljs lazy loading
- Routing —
tyrell.router - i18n —
tyrell.i18n - Layout —
tyrell.layoutcontainer-aware breakpoints
- HTMX Guide —
hx-*attributes + form-associated<ty-*> - Datastar Guide — Datastar + SSE patterns
Add to deps.edn:
{:deps {dev.gersak/tyrell {:mvn/version "1.0.0-RC6"}}} ; icons come transitivelyThat single dep brings:
- Routing, i18n, layout, icon registry, shim —
tyrell.router,tyrell.i18n,tyrell.layout,tyrell.icons,tyrell.shim - The
tyrell.componentsshim that side-effect-imports the npm package - 12,000+ tree-shakeable icons (
tyrell.lucide,tyrell.heroicons.*,tyrell.material.*,tyrell.fontawesome.*)
The npm tyrell-components package is declared in this artifact's deps.cljs, so shadow-cljs auto-installs it on first build.
| You're using | Guide |
|---|---|
| Reagent, re-frame, UIx, Helix | QUICKSTART → Track A — tyrell.react wrappers |
| Replicant, vanilla CLJS, server-rendered | REPLICANT_TY_GUIDE — raw <ty-*> elements |
<ty-icon name="check"> is a runtime registry lookup. Register the icons you reference at app startup:
(ns my-app.core
(:require [tyrell.components] ; side-effect: registers all <ty-*> elements
[tyrell.icons :as icons]
[tyrell.lucide :as lucide]
[tyrell.heroicons.outline :as ho]))
(defn register-icons! []
(icons/register!
{:check lucide/check
:search lucide/search
:user ho/user-circle}))Shadow-cljs :advanced removes unused icons automatically — you only pay for what you reference. See CLOJURESCRIPT_GUIDE → Icon registration.
Use tyrell.shim to turn any ClojureScript render function into a Web Component:
(ns app.components
(:require [replicant.dom :as d]
[tyrell.shim :as shim]))
(defn greeting [name]
[:div.ty-elevated.p-4.rounded-lg
[:h2.ty-text+ "Hello, " name "!"]
[:ty-button {:flavor "primary"} "Wave"]])
(defn render! [^js el]
(d/render (shim/ensure-shadow el)
(greeting (or (shim/attr el "name") "World"))))
(shim/define! "my-greeting"
{:observed [:name]
:connected render!
:attr (fn [el _] (render! el))})<my-greeting name="Clojure"></my-greeting>Component Building Guide → | Code Splitting →
| Component | Description |
|---|---|
ty-button |
Semantic buttons with flavors, sizes, and icon slots |
ty-input |
Text input with labels, validation, numeric formatting, debounce |
ty-textarea |
Multi-line text with auto-resize and character count |
ty-checkbox |
Styled checkbox with indeterminate state |
ty-switch |
Toggle switch primitive |
ty-radio-group / ty-radio |
Exclusive single-choice selection |
ty-dropdown |
Searchable select with keyboard nav and mobile modal |
ty-multiselect |
Multi-select with tags and search |
ty-calendar |
Full calendar with date selection and form integration |
ty-date-picker |
Calendar dropdown for date input |
ty-tabs / ty-tab |
Carousel tabs with smooth animations |
ty-wizard / ty-step |
Step-by-step wizard with progress tracking |
ty-modal |
Native dialog with backdrop and focus management |
ty-popup |
Anchored popover with smart positioning |
ty-tooltip |
Hover tooltips with placement options |
ty-icon |
SVG icons from Lucide, Heroicons, Material, FontAwesome |
ty-tag |
Removable tags for selections |
ty-copy |
Click-to-copy with visual feedback |
ty-scroll-container |
Scrollable area with fade indicators |
ty-resize-observer |
Self-observing element with debounce |
See all components in action →
- Documentation & Examples
- GitHub
- NPM tyrell-components
- NPM tyrell-react
- Clojars dev.gersak/tyrell
- Clojars dev.gersak/tyrell-icons
MIT License