Tooey
a token-efficient ui library https://vijaypemmaraju.github.io/tooey
Install / Use
/learn @vijaypemmaraju/TooeyREADME
tooey
token-efficient ui library ecosystem for llms
~75% fewer output tokens than react | ~10kb minified | 0 deps
benchmarks
comprehensive benchmarks comparing tooey vs react are available in packages/ui/benchmarks/BENCHMARK_RESULTS.md.
token efficiency
tokens counted using gpt-4 tokenizer. lower is better for llm cost and context.
| component | tooey | react | savings | |-----------|-------|-------|---------| | counter | 51 | 102 | 50% | | todo list | 87 | 194 | 55% | | form | 131 | 240 | 45% | | tabs | 96 | 116 | 17% | | modal | 127 | 178 | 29% | | data table | 83 | 120 | 31% | | shopping cart | 140 | 282 | 50% | | wizard | 216 | 356 | 39% | | total | 931 | 1588 | 41% |
runtime performance
benchmarks run in jsdom environment.
| benchmark | result | |-----------|--------| | render 1000 items | ~20ms | | 10,000 state updates | ~69ms (6.9μs/update) | | signal with 100 subscribers | ~10ms for 10k updates |
bundle size
| library | minified | gzipped | |---------|----------|---------| | tooey | ~10 KB | ~4 KB | | react + reactdom | ~140 KB | ~45 KB | | preact | ~10 KB | ~4 KB | | vue 3 | ~40 KB | ~16 KB |
run benchmarks locally:
cd packages/ui
pnpm benchmark # full benchmark with report
pnpm benchmark:test # performance tests via vitest
packages
| package | description | version |
|---------|-------------|---------|
| @tooey/ui | core library | |
| @tooey/components | component library | coming soon |
| @tooey/claude-plugin | claude code plugin |
|
quick start
npm install @tooey/ui
import { render, vs, hs, tx, bt } from '@tooey/ui';
render(document.getElementById('app'), {
s: { n: 0 },
r: [vs, [[tx, { $: 'n' }], [hs, [[bt, '-', { c: 'n-' }], [bt, '+', { c: 'n+' }]], { g: 8 }]], { g: 8 }]
});
documentation
claude code plugin
generate token-efficient ui specs directly in claude code.
install
# add the tooey marketplace
claude plugin marketplace add https://raw.githubusercontent.com/vijaypemmaraju/tooey/main/marketplace.json
# install the plugin
claude plugin install tooey
use
/tooey:ui a counter with increment and decrement buttons
/tooey:ui a login form with username and password fields
/tooey:ui a modal dialog with close button
see @tooey/claude-plugin for more installation options.
ecosystem
tooey is designed to be extensible while preserving token efficiency.
design principles
- functions over registration - custom components are just functions, no registry
- composition over configuration - build complex from simple primitives
- short names stay short - extensions follow abbreviation patterns
- tree-shakeable - everything importable individually
- zero overhead - extensions don't bloat base library
- llm-friendly - patterns that ai can learn and generate efficiently
function components
no registration needed. a component is just a function that returns a NodeSpec.
type Component<P = {}> = (props?: P, children?: NodeSpec[]) => NodeSpec;
// define a Card component
const Card = (props = {}, children = []) => [
vs,
children,
{ bg: '#fff', p: 16, r: 8, sh: '0 2px 4px rgba(0,0,0,0.1)', ...props }
];
// usage in spec (token-efficient)
{ r: [Card, [[tx, 'Hello']], { bg: '#f0f0f0' }] }
// or with helper
{ r: Card({ bg: '#f0f0f0' }, [[tx, 'Hello']]) }
component with state
// components can define their own state keys
const Counter = (props = {}) => ({
// state contribution (merged with parent spec.s)
s: { count: props.initial || 0 },
// component tree
r: [hs, [
[bt, '-', { c: 'count-' }],
[tx, { $: 'count' }],
[bt, '+', { c: 'count+' }]
], { g: 8, ...props }]
});
// usage - returns TooeySpec, can be spread or rendered directly
render(el, Counter({ initial: 5 }))
component libraries
third-party packages export components following naming conventions.
naming convention
| full name | abbrev | rule |
|-----------|--------|------|
| Card | Cd | first + last consonant |
| Modal | Mdl | consonant cluster |
| Tabs | Tbs | plural → add 's' |
| Alert | Al | first 2 letters |
| Dropdown | Dd | first letter + key consonant |
| Tooltip | Tt | double first letter |
import { Cd, Mdl, Al } from '@tooey/components';
{ r: [Cd, [[tx, 'Content']], { variant: 'outlined' }] }
theming
option a: theme objects (zero overhead)
// define theme
const theme = {
card: { bg: '#fff', r: 8, sh: '0 2px 8px rgba(0,0,0,0.1)' },
btn: { bg: '#007bff', fg: '#fff', r: 4, p: '8 16' },
input: { bw: 1, bc: '#ccc', r: 4, p: 8 }
};
// apply via spread
[vs, [...], { ...theme.card, p: 24 }]
option b: theme provider (runtime)
// new api
const { render, theme } = createTooey({
colors: { primary: '#007bff', danger: '#dc3545' },
spacing: { sm: 8, md: 16, lg: 24 },
radius: { sm: 4, md: 8, lg: 16 }
});
// usage with $ prefix for theme values
[bt, 'Save', { bg: '$primary', p: '$md', r: '$sm' }]
plugin system
lightweight hooks for cross-cutting concerns.
plugin interface
interface TooeyPlugin {
name: string;
// lifecycle hooks
onInit?(instance: TooeyInstance): void;
onDestroy?(instance: TooeyInstance): void;
// render hooks
beforeRender?(spec: NodeSpec, ctx: RenderContext): NodeSpec;
afterRender?(el: HTMLElement, spec: NodeSpec): void;
// state hooks
onStateChange?(key: string, oldVal: unknown, newVal: unknown): void;
// extend instance
extend?: Record<string, Function>;
}
example plugins
// router plugin
const routerPlugin = {
name: 'router',
onInit(instance) {
instance.state.route = signal(location.pathname);
window.addEventListener('popstate', () => {
instance.set('route', location.pathname);
});
},
extend: {
navigate(path) {
history.pushState({}, '', path);
this.set('route', path);
}
}
};
// logger plugin
const loggerPlugin = {
name: 'logger',
onStateChange(key, oldVal, newVal) {
console.log(`[${key}]`, oldVal, '→', newVal);
}
};
// usage
const app = render(el, spec, { plugins: [routerPlugin, loggerPlugin] });
app.navigate('/about');
slots & composition
for complex component composition.
// component with slots
const Layout = (props, children) => {
const { header, footer, ...rest } = props;
return [vs, [
header && [dv, [header], { cls: 'header' }],
[dv, children, { cls: 'main', s: { flex: 1 } }],
footer && [dv, [footer], { cls: 'footer' }]
], { h: '100vh', ...rest }];
};
// usage
[Layout, [
[tx, 'Main content']
], {
header: [hs, [[tx, 'Logo'], [tx, 'Nav']], { jc: 'sb' }],
footer: [tx, '© 2024']
}]
state modules
reusable state patterns.
// define a state module
const formState = (fields) => ({
s: {
values: Object.fromEntries(fields.map(f => [f, ''])),
errors: {},
touched: {},
submitting: false
},
actions: {
setValue: (field, value) => ['values', '.', [field, value]],
setError: (field, error) => ['errors', '.', [field, error]],
submit: () => ['submitting', '!', true]
}
});
// usage
const form = formState(['email', 'password']);
render(el, {
s: { ...form.s, otherState: 123 },
r: [vs, [
[In, '', { v: { $: 'values.email' }, x: form.actions.setValue.bind(null, 'email') }],
[bt, 'Submit', { c: form.actions.submit }]
]]
});
computed state
derived values that update automatically.
import { signal, computed, render } from '@tooey/ui';
const items = signal([{ price: 10 }, { price: 20 }]);
const total = computed(() => items().reduce((sum, i) => sum + i.price, 0));
// total() automatically updates when items changes
async state
handle loading states elegantly.
// async$ helper
function async$(promise, { loading, error }) {
return {
s: { data: null, loading: true, error: null },
init: async (instance) => {
try {
const data = await promise;
instance.set('data', data);
} catch (e) {
instance.set('error', e.message);
} finally {
instance.set('loading', false);
}
}
};
}
// usage
const userSpec = async$(fetch('/api/user').then(r => r.json()), {
loading: [tx, 'Loading...'],
error: [tx, { $: 'error' }, { fg: 'red' }]
});
render(el, {
s: userSpec.s,
r: { '?': 'loading',
t: userSpec.loading,
e: { '?': 'error', t: userSpec.error, e: [tx, { $: 'data.name' }] }
}
});
type-safe props
typescript support for custom components.
interface CardProps extends Props {
variant?: 'filled' | 'outlined' | 'elevated';
clickable?: boolean;
}
const Card: Component<CardProps> = (props = {}, children = []) => {
const { variant = 'elevated', clickable, ...rest } = props;
const styles = {
filled: { bg: '#f5f5f5' },
outlined: { bw: 1, bc: '#ddd' },
elevated: { sh: '0 2px 8px rgba(0,0,0,0.1)' }
};
return [vs, children, {
...styles[variant],
r: 8,
p: 16,
cur: clickable ? 'pointer' : undefined,
...rest
}];
};
token efficiency analysis
| pattern | tokens | notes |
|---------|--------|-------|
| [Card, [...], {}] | ~5 | same as built-in |
| `Card({
Related Skills
node-connect
352.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
111.1kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
352.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
352.2kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
