v0.2.5 — Now with AI Components

The AI-Native Web Framework

Write less. Build faster. No imports. Pure magic.

Counter.speck
<Counter>
  <state count={0} />
  
  <button onClick={() => state.count.value++}>
    Count: {state.count.value}
  </button>
</Counter>
Get Started Read the Docs
0 Imports needed
~2KB Runtime
Possibilities

Everything you need.
Nothing you don't.

🤖

AI-Native

Built-in SpeckAgent class for Claude integration. Build AI-powered apps in minutes, not hours.

const agent = new SpeckAgent({ purpose: '...' });

Zero Imports

Components auto-discover each other. Just use <MyComponent /> anywhere.

🎯

Fine-Grained Reactivity

Powered by Preact Signals. Only what changes re-renders. No virtual DOM diffing.

🛣️

Built-in Router

Client-side routing with dynamic params. No extra packages needed.

Async Made Easy

<async>, <then>, <catch>, <loading> — handle promises declaratively.

🧩

Slots & Props

Flexible composition with named slots and prop spreading. Build reusable components.

Up and running in 30 seconds

1

Create your app

$ npm create speck-app my-app
$ cd my-app
2

Add your API key (optional, for AI features)

# Edit .env
VITE_ANTHROPIC_API_KEY=sk-ant-...
3

Start building

$ npm run dev

 Vite server running at http://localhost:5174
 Compiler watching for changes
 AI Agent proxy ready

See what's possible

<Counter>
  <state count={0} />
  
  <script>
    const increment = () => state.count.value++;
    const decrement = () => state.count.value--;
  </script>

  <div>
    <h2>Count: {state.count.value}</h2>
    <button onClick={decrement}>-</button>
    <button onClick={increment}>+</button>
  </div>
</Counter>

Reactive State with Signals

Declare state with <state> tags. Access values with .value. Updates are fine-grained — only what changes re-renders.

<Welcome>
  <state isLoggedIn={false} />
  
  <if condition={state.isLoggedIn.value}>
    <p>Welcome back!</p>
    <Dashboard />
  </if>
  
  <if condition={!state.isLoggedIn.value}>
    <p>Please log in.</p>
    <LoginForm />
  </if>
</Welcome>

Declarative Conditionals

Use <if condition={...}> for conditional rendering. Clean, readable, no ternary soup.

<UserList>
  <state users={[
    { id: 1, name: "Alice" },
    { id: 2, name: "Bob" }
  ]} />
  
  <ul>
    <loop of={state.users.value} let={user}>
      <li>{user.name}</li>
    </loop>
  </ul>
</UserList>

Simple List Rendering

Iterate with <loop of={array} let={item}>. No .map() callbacks needed.

<App>
  <Router>
    <route path="/">
      <HomePage />
    </route>
    
    <route path="/user/:id" let={params}>
      <UserProfile userId={params.id} />
    </route>
  </Router>
</App>

Built-in Routing

Client-side routing with dynamic params. No react-router needed. Just <Router> and <route>.

<DataLoader>
  <async promise={fetch('/api/data').then(r => r.json())}>
    <loading>
      <p>Loading...</p>
    </loading>
    
    <then let={data}>
      <p>Got: {data.name}</p>
    </then>
    
    <catch let={error}>
      <p>Error: {error.message}</p>
    </catch>
  </async>
</DataLoader>

Declarative Async

Handle promises with <async>. Built-in loading, success, and error states. No useEffect gymnastics.

<AskClaude>
  <state message={""} />
  <state response={""} />
  <state loading={false} />

  <input value={state.message.value} />
  <button onClick={async () => {
    state.loading.value = true;
    const agent = new SpeckAgent({
      purpose: 'You are helpful.',
      model: 'claude-sonnet-4-20250514'
    });
    const result = await agent.send(state.message.value);
    state.response.value = result.content;
    state.loading.value = false;
  }}>
    {state.loading.value ? 'Thinking...' : 'Ask'}
  </button>
  <p>{state.response.value}</p>
</AskClaude>

AI-Native with SpeckAgent

Built-in SpeckAgent class for Claude integration. Add AI chat to your app in minutes. Streaming support included.

Less code. More clarity.

React
import { useState } from 'react';
import Button from './Button';
import Header from './Header';

export default function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <Header />
      <p>Count: {count}</p>
      <Button onClick={() => setCount(c => c + 1)}>
        Increment
      </Button>
    </div>
  );
}
⚡ Speck.js
<Counter>
  <state count={0} />
  
  <div>
    <Header />
    <p>Count: {state.count.value}</p>
    <Button onClick={() => state.count.value++}>
      Increment
    </Button>
  </div>
</Counter>

Ready to build something amazing?

Join the future of web development. AI-native. Zero imports. Pure magic.