Cedar allows your agent to understand what is happening in your application, and the ability to change it. Registering a local state in Cedar is the first step to giving your agent frontend context, letting users point out parts of the UI to the agent, and letting agents manipulate state. In other words, it allows your agent to read and write to the local react state. To understand how we internally execute and render actions on your state (and how to override default behavior and rendering for state changes), read SetState Response Processing and SetState Message Rendering.

useRegisterState Hook

Register your existing React state with Cedar using the useRegisterState hook:
import { useRegisterState } from 'cedar-os';

const [todos, setTodos] = useState([
	{ id: 1, text: 'Learn Cedar-OS', completed: false },
	{ id: 2, text: 'Build amazing AI apps', completed: false },
]);

// Now the agent will know what the state is, how to change it,
// and have access to calling these setters
useRegisterState({
	key: 'todos',
	description: 'A list of todo items that users can check off',
	value: todos,
	setValue: setTodos,
	stateSetters: {
		addTodo: {
			name: 'addTodo',
			description: 'Add a new todo item',
			execute: (currentTodos, args: { text: string }) => {
				const newTodo = {
					id: Date.now(),
					text: args.text,
					completed: false,
				};
				setTodos([...currentTodos, newTodo]);
			},
		},
		toggleTodo: {
			name: 'toggleTodo',
			description: 'Toggle completion status of a todo',
			execute: (currentTodos, args: { id: number }) => {
				setTodos(
					currentTodos.map((todo) =>
						todo.id === args.id ? { ...todo, completed: !todo.completed } : todo
					)
				);
			},
		},
		removeTodo: {
			name: 'removeTodo',
			description: 'Remove a todo item',
			execute: (currentTodos, args: { id: number }) => {
				setTodos(currentTodos.filter((todo) => todo.id !== args.id));
			},
		},
	},
});

useCedarState

Use useCedarState to create and manage state directly in the Cedar store. It directly replaces useState and works the exact same way, but
import { useCedarState } from 'cedar-os';

function TodoComponent() {
	const [todos, setTodos] = useCedarState(
		'todos',
		[
			{ id: 1, text: 'Learn Cedar-OS', completed: false },
			{ id: 2, text: 'Build amazing AI apps', completed: false },
		],
		'A list of todo items that users can check off',
		{
			addTodo: {
				name: 'addTodo',
				description: 'Add a new todo item',
				execute: (currentTodos, args: { text: string }) => {
					const newTodo = {
						id: Date.now(),
						text: args.text,
						completed: false,
					};
					setTodos([...currentTodos, newTodo]);
				},
			},
			toggleTodo: {
				name: 'toggleTodo',
				description: 'Toggle completion status of a todo',
				execute: (currentTodos, args: { id: number }) => {
					setTodos(
						currentTodos.map((todo) =>
							todo.id === args.id
								? { ...todo, completed: !todo.completed }
								: todo
						)
					);
				},
			},
			removeTodo: {
				name: 'removeTodo',
				description: 'Remove a todo item',
				execute: (currentTodos, args: { id: number }) => {
					setTodos(currentTodos.filter((todo) => todo.id !== args.id));
				},
			},
		}
	);

	return (
		<div>
			<h2>My Todos</h2>
			{todos.map((todo) => (
				<div key={todo.id}>
					<input
						type='checkbox'
						checked={todo.completed}
						onChange={() => {
							// You can call the setter directly or use custom setters
							setTodos(
								todos.map((t) =>
									t.id === todo.id ? { ...t, completed: !t.completed } : t
								)
							);
						}}
					/>
					<span>{todo.text}</span>
					<button
						onClick={() => {
							setTodos(todos.filter((t) => t.id !== todo.id));
						}}>
						Delete
					</button>
				</div>
			))}
			<button
				onClick={() => {
					const newTodo = {
						id: Date.now(),
						text: 'New todo',
						completed: false,
					};
					setTodos([...todos, newTodo]);
				}}>
				Add Todo
			</button>
		</div>
	);
}

Accessing Cedar State Functions

executeStateSetter

Execute state setter functions programmatically using the Cedar store:
import { useCedarStore } from 'cedar-os';

function TodoActions() {
	const executeStateSetter = useCedarStore((state) => state.executeStateSetter);

	const handleAddTodo = async () => {
		executeStateSetter('todos', 'addTodo', { text: 'Learn about Cedar State' });
	};

	const handleToggleTodo = async () => {
		executeStateSetter('todos', 'toggleTodo', { id: 1 });
	};

	const handleRemoveTodo = async () => {
		executeStateSetter('todos', 'removeTodo', { id: 1 });
	};

	return (
		<div>
			<button onClick={handleAddTodo}>Add Todo</button>
			<button onClick={handleToggleTodo}>Toggle First Todo</button>
			<button onClick={handleRemoveTodo}>Remove First Todo</button>
		</div>
	);
}
Breaking Change in v0.1.11: Parameter handling has changed. State setters now receive arguments as a single object parameter instead of multiple spread parameters. Update your setter functions accordingly.
Agent Actions: State setters can be automatically invoked by your agent through structured responses. Learn more about SetState Response Processing and SetState Message Rendering to see how agents can execute these setters and display the results.
Automatic Cleanup: States are automatically unregistered when components unmount, ensuring clean state management without manual cleanup.

Schema Validation with Zod

Cedar OS has runtime validation of setter arguments using Zod schemas:
import { z } from 'zod';
import { useRegisterState } from 'cedar-os';

// Define schemas for your setter arguments
const addTodoSchema = z.object({
	text: z.string().min(1, 'Todo text cannot be empty'),
});

const toggleTodoSchema = z.object({
	id: z.number().positive('ID must be positive'),
});

const [todos, setTodos] = useState([]);

useRegisterState({
	key: 'todos',
	value: todos,
	setValue: setTodos,
	stateSetters: {
		addTodo: {
			name: 'addTodo',
			description: 'Add a new todo item',
			argsSchema: addTodoSchema,
			execute: (currentTodos, args) => {
				// args is now type-safe and validated
				const newTodo = {
					id: Date.now(),
					text: args.text, // TypeScript knows this is a string
					completed: false,
				};
				setTodos([...currentTodos, newTodo]);
			},
		},
		toggleTodo: {
			name: 'toggleTodo',
			description: 'Toggle completion status of a todo',
			argsSchema: toggleTodoSchema,
			execute: (currentTodos, args) => {
				// args.id is validated as a positive number
				setTodos(
					currentTodos.map((todo) =>
						todo.id === args.id ? { ...todo, completed: !todo.completed } : todo
					)
				);
			},
		},
	},
});

getCedarState

Retrieve the current state value for a registered Cedar state key:
import { getCedarState } from 'cedar-os';

// Get current todos state
const currentTodos = getCedarState('todos');
console.log('Current todos:', currentTodos);

// Use in a function
function logTodoCount() {
	const todos = getCedarState('todos');
	if (todos && Array.isArray(todos)) {
		console.log(`You have ${todos.length} todos`);
	}
}

// Check if state exists
function checkTodosExist() {
	const todos = getCedarState('todos');
	if (todos) {
		console.log('Todos state is available');
	} else {
		console.log('Todos state not found');
	}
}

setCedarState

Directly update the state value for a registered Cedar state key:
import { setCedarState } from 'cedar-os';

// Set new todos state
const newTodos = [
	{ id: 1, text: 'Updated todo', completed: true },
	{ id: 2, text: 'Another todo', completed: false },
];
setCedarState('todos', newTodos);

// Update based on current state
const currentTodos = getCedarState('todos');
if (currentTodos && Array.isArray(currentTodos)) {
	const updatedTodos = currentTodos.map((todo) => ({
		...todo,
		completed: true,
	}));
	setCedarState('todos', updatedTodos);
}

// Reset state
setCedarState('todos', []);

Exposing states to your agent

Now that your application states are registered in Cedar, learn how to expose just the right pieces of them to your agent:
  • Agent Input Context – orchestrate what context is sent alongside every user message, including state subscriptions and manual entries. See Agent Input Context.
  • Mentions – let users reference individual state items with simple @ mentions. See Mentions.
These docs walk you through granting the agent scoped, real-time access to your frontend data.