Task
The Task
component provides a structured way to display task lists or workflow progress with collapsible details, status indicators, and progress tracking. It consists of a main Task
container with TaskTrigger
for the clickable header and TaskContent
for the collapsible content area.
Installation
npx ai-elements@latest add task
Usage
import { Task, TaskContent, TaskItem, TaskItemFile, TaskTrigger,} from '@/components/ai-elements/task';
<Task className="w-full"> <TaskTrigger title="Found project files" /> <TaskContent> <TaskItem> Read <TaskItemFile>index.md</TaskItemFile> </TaskItem> </TaskContent></Task>
Usage with AI SDK
Build a mock async programming agent using experimental_generateObject
.
Add the following component to your frontend:
app/page.tsx
'use client';
import { experimental_useObject as useObject } from '@ai-sdk/react';import { Task, TaskItem, TaskItemFile, TaskTrigger, TaskContent,} from '@/components/ai-elements/task';import { Button } from '@/components/ui/button';import { tasksSchema } from '@/app/api/task/route';import { SiReact, SiTypescript, SiJavascript, SiCss, SiHtml5, SiJson, SiMarkdown,} from '@icons-pack/react-simple-icons';
const iconMap = { react: { component: SiReact, color: '#149ECA' }, typescript: { component: SiTypescript, color: '#3178C6' }, javascript: { component: SiJavascript, color: '#F7DF1E' }, css: { component: SiCss, color: '#1572B6' }, html: { component: SiHtml5, color: '#E34F26' }, json: { component: SiJson, color: '#000000' }, markdown: { component: SiMarkdown, color: '#000000' },};
const TaskDemo = () => { const { object, submit, isLoading } = useObject({ api: '/api/agent', schema: tasksSchema, });
const handleSubmit = (taskType: string) => { submit({ prompt: taskType }); };
const renderTaskItem = (item: any, index: number) => { if (item?.type === 'file' && item.file) { const iconInfo = iconMap[item.file.icon as keyof typeof iconMap]; if (iconInfo) { const IconComponent = iconInfo.component; return ( <span className="inline-flex items-center gap-1" key={index}> {item.text} <TaskItemFile> <IconComponent color={item.file.color || iconInfo.color} className="size-4" /> <span>{item.file.name}</span> </TaskItemFile> </span> ); } } return item?.text || ''; };
return ( <div className="max-w-4xl mx-auto p-6 relative size-full rounded-lg border h-[600px]"> <div className="flex flex-col h-full"> <div className="flex gap-2 mb-6 flex-wrap"> <Button onClick={() => handleSubmit('React component development')} disabled={isLoading} variant="outline" > React Development </Button> </div>
<div className="flex-1 overflow-auto space-y-4"> {isLoading && !object && ( <div className="text-muted-foreground">Generating tasks...</div> )}
{object?.tasks?.map((task: any, taskIndex: number) => ( <Task key={taskIndex} defaultOpen={taskIndex === 0}> <TaskTrigger title={task.title || 'Loading...'} /> <TaskContent> {task.items?.map((item: any, itemIndex: number) => ( <TaskItem key={itemIndex}> {renderTaskItem(item, itemIndex)} </TaskItem> ))} </TaskContent> </Task> ))} </div> </div> </div> );};
export default TaskDemo;
Add the following route to your backend:
app/api/agent.ts
import { streamObject } from 'ai';import { z } from 'zod';
export const taskItemSchema = z.object({ type: z.enum(['text', 'file']), text: z.string(), file: z .object({ name: z.string(), icon: z.string(), color: z.string().optional(), }) .optional(),});
export const taskSchema = z.object({ title: z.string(), items: z.array(taskItemSchema), status: z.enum(['pending', 'in_progress', 'completed']),});
export const tasksSchema = z.object({ tasks: z.array(taskSchema),});
// Allow streaming responses up to 30 secondsexport const maxDuration = 30;
export async function POST(req: Request) { const { prompt } = await req.json();
const result = streamObject({ model: 'openai/gpt-4o', schema: tasksSchema, prompt: `You are an AI assistant that generates realistic development task workflows. Generate a set of tasks that would occur during ${prompt}.
Each task should have: - A descriptive title - Multiple task items showing the progression - Some items should be plain text, others should reference files - Use realistic file names and appropriate file types - Status should progress from pending to in_progress to completed
For file items, use these icon types: 'react', 'typescript', 'javascript', 'css', 'html', 'json', 'markdown'
Generate 3-4 tasks total, with 4-6 items each.`, });
return result.toTextStreamResponse();}
Features
- Visual icons for pending, in-progress, completed, and error states
- Expandable content for task descriptions and additional information
- Built-in progress counter showing completed vs total tasks
- Optional progressive reveal of tasks with customizable timing
- Support for custom content within task items
- Full type safety with proper TypeScript definitions
- Keyboard navigation and screen reader support
Props
<Task />
[...props]?:
React.ComponentProps<typeof Collapsible>
Any other props are spread to the root Collapsible component.
<TaskTrigger />
title:
string
The title of the task that will be displayed in the trigger.
[...props]?:
React.ComponentProps<typeof CollapsibleTrigger>
Any other props are spread to the CollapsibleTrigger component.
<TaskContent />
[...props]?:
React.ComponentProps<typeof CollapsibleContent>
Any other props are spread to the CollapsibleContent component.
<TaskItem />
[...props]?:
React.ComponentProps<"div">
Any other props are spread to the underlying div.
<TaskItemFile />
[...props]?:
React.ComponentProps<"div">
Any other props are spread to the underlying div.