WorkRunner Documentation
Step 2of 3

Build Your First Construct

Let's build a simple todo list to learn the Construct patterns. This will take about 5 minutes.

1. Create Your File

Create a new file called App.tsx. This is the only file you need.

$ touch App.tsx

2. Generate with AI

Open Claude Code or OpenCode and ask it to build your Construct:

Try this prompt:

Build me a todo list Construct. It should let users add tasks, mark them complete, and delete them. Save everything to localStorage.

The AI will generate a complete Construct following all our standards because it has the @useworkapp/construct-skill installed.

3. Understand the Pattern

Here's what a complete Construct looks like. Let's break it down:

Imports

"use client";

import { useState, useEffect } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import { Label } from "@/components/ui/label";
import { Plus, Trash2 } from "lucide-react";

Always use "use client" at the top. Import only from shadcn/ui and lucide-react.

Types and Storage Key

interface Todo {
  id: string;
  text: string;
  completed: boolean;
}

const STORAGE_KEY = "work-construct-todo-list";

Always prefix your storage key with work-construct-to avoid conflicts.

State with localStorage

export default function App() {
  const [todos, setTodos] = useState<Todo[]>(() => {
    if (typeof window === "undefined") return [];
    const saved = localStorage.getItem(STORAGE_KEY);
    return saved ? JSON.parse(saved) : [];
  });

  useEffect(() => {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(todos));
  }, [todos]);

Initialize state from localStorage and save on every change. Check for window to handle SSR.

JSX Structure

return (
  <div className="min-h-screen bg-background p-4 sm:p-6">
    <Card className="mx-auto max-w-md">
      <CardHeader>
        <CardTitle>Todo List</CardTitle>
      </CardHeader>
      <CardContent className="space-y-4">
        {/* Form and list here */}
      </CardContent>
    </Card>
  </div>
);

Use Tailwind classes for styling. Always include responsive padding (p-4 sm:p-6).

Accessibility

<Button onClick={addTodo} aria-label="Add task">
  <Plus className="h-4 w-4" />
</Button>

<Checkbox
  id={todo.id}
  checked={todo.completed}
  onCheckedChange={() => toggleTodo(todo.id)}
/>
<Label htmlFor={todo.id}>{todo.text}</Label>

Add aria-label to icon buttons. Connect labels to inputs with matching id/htmlFor.

4. Validate Your Code

Run the validator to check your Construct:

$ construct-skill validate ./App.tsx

╔══════════════════════════════════════════════════════════════╗
║ WORK CONSTRUCT VALIDATOR ║
╚══════════════════════════════════════════════════════════════╝

✓ Construct passes all validations!

Common Mistakes to Avoid

Don't use fetch or axios

Constructs run in a sandbox without network access. Use localStorage only.

Don't use inline styles

Use Tailwind classes instead of style={{}}.

Don't forget the storage key prefix

Always use work-construct-your-name as your localStorage key.

Don't skip accessibility

Icon buttons need aria-label. Inputs need associated labels.

Next: Submit for Review

Submit your Construct and start earning.