Aha! Builder applications are built with React. The client framework gives your application a small, consistent set of utilities for calling server functions, fetching and mutating data with automatic optimistic updates, navigating between pages, and reporting errors. Elle uses these whenever it builds your application's interface.
This reference documents the client API: the data hooks, useServerMutation, the router utilities, and captureError. Read it to understand how your application's front end talks to its server, to review the code Elle generates, or to ground an AI assistant in the specifics — reference this article with Elle or any LLM so it knows exactly how the client framework works in Aha! Builder.
Click any of the following links to skip ahead:
Overview
Entry point for the client code.
invokeDataFunction()
function invokeDataFunction(
functionName: string,
args?: unknown[]
): Promise<unknown>;Calls a server function by name and returns the result. Use this for imperative calls in event handlers or non-component code.
useDataFunction()
function useDataFunction<T = unknown>(
functionName: string,
args?: unknown[]
): DataFunctionResult<T>;React hook that fetches data from a server function on mount and re-fetches when args change.
Returns { data, loading, error, refetch, mutate }.
mutate(updater)
Updates the local data immediately without a server call. Prefer useServerMutation for optimistic updates in React components; it runs the optimistic update before the server call, captures errors, and refetches on error by default.
updater can be a new value or a function that receives the previous data and returns the new data. Use mutate(updater) directly only for lower-level custom flows that cannot use useServerMutation.
useServerMutation()
function useServerMutation<Input, Output, Data>(
serverFunction: (input: Input) => Promise<Output>,
options?: {
query?: { mutate; refetch };
optimistic?: (prev: Data | undefined, input: Input) => Data | undefined;
refetchOnSuccess?: boolean;
onSuccess?: (result: Output, input: Input) => unknown | Promise<unknown>;
onError?: (error: Error, input: Input) => unknown | Promise<unknown>;
}
);Wraps a server function (imported from @/server) into a React mutation hook. Returns { mutate, loading, isPending }. The hook runs the optimistic updater before the server call, captures errors, and refetches the supplied query on error to roll back.
import { toast } from 'sonner';
import { useServerMutation } from '@aha-app/builder-core';
import { useGetTodos, toggleTodo, deleteTodo } from '@/server';
const todosQuery = useGetTodos();
const { data: todos } = todosQuery;
const toggle = useServerMutation(toggleTodo, {
query: todosQuery,
optimistic: (prev: any, input) =>
prev?.map(t => (t.id === input.id ? { ...t, completed: !t.completed } : t)),
onError: () => toast.error('Failed to update todo'),
});
const remove = useServerMutation(deleteTodo, {
query: todosQuery,
optimistic: (prev: any, input) => prev?.filter(t => t.id !== input.id),
onError: () => toast.error('Failed to delete todo'),
});Router utilities
function navigate(to: string): void;
function useParams(): RouteParams;
function useNavigate(): (to: string) => void;
function useLocation(): string;Link
<Link to='/path'>label</Link>A client-side navigation link that intercepts clicks and uses pushState instead of a full page load.
captureError()
import { captureError } from '@aha-app/builder-core';
function captureError(error: Error): void;Reports an error to the application's monitoring service. Use this in catch blocks for server function calls, event handlers, and any async operations where errors would otherwise be silently swallowed.
The React error boundary already calls captureError automatically for render errors. Use it explicitly for errors in event handlers, async callbacks, and imperative code that the error boundary cannot catch.
import { captureError } from '@aha-app/builder-core';
import { toast } from 'sonner';
import { createItem } from '@/server';
const handleSubmit = async () => {
try {
await createItem({ name });
} catch (error) {
captureError(error as Error);
toast.error('Failed to create item');
}
};Authentication
Re-exported from the authentication package:
currentUser()— synchronously returns the current logged-in user, ornull.redirectToLogin()— redirects the browser to the login flow.redirectToLogout()— redirects the browser to log out.Authenticate— React component that gates its children behind authentication.
Build it with Elle
Describe the capability you want in plain language, and Elle, the Aha! Builder AI assistant, builds it into your application using the framework above.
New to this? Open Elle, ask what it can do, and try a capability in your application today.