How to write autosaving forms to create and update CoValues

This guide shows you a simple and powerful way to implement forms for creating and updating CoValues.

We'll build:

  1. An update form that saves changes as you make them, removing the need for a save button.
  2. A create form that autosaves your changes into a draft, so you can come back to it later.

See the full example here.

Note: If you do need a save button on your update form, this guide is not for you. Another option is to use react-hook-form.

Updating a CoValue

To update a CoValue, we simply assign the new value directly as changes happen. These changes are synced to the server.

<input
  type="text"
  value={order.name}
  onChange={(e) => order.$jazz.set("name", e.target.value)}
/>

It's that simple!

Creating a CoValue

However, when creating a CoValue, the CoValue does not exist yet, so we don't have the advantages previously mentioned.

There's a way around this, and it provides unexpected benefits too.

Using a Draft CoValue

Let's say we have a CoValue called BubbleTeaOrder. We can create a "draft" CoValue, which is an empty version of a BubbleTeaOrder, that we can then modify when we are "creating" a new CoValue.

A DraftBubbleTeaOrder is essentially a copy of BubbleTeaOrder, but with all the fields made optional.

// schema.ts
export const 
const BubbleTeaOrder: co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>
BubbleTeaOrder
= import coco.
map<{
    name: z.z.ZodString;
}>(shape: {
    name: z.z.ZodString;
}): co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>
export map
map
({
name: z.z.ZodStringname: import zz.
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString (+1 overload)
export string
string
(),
}); export type
type BubbleTeaOrder = {
    readonly name: string;
} & CoMap
BubbleTeaOrder
= import coco.
type loaded<T extends CoValueClassOrSchema, R extends ResolveQuery<T> = true> = R extends boolean | undefined ? NonNullable<InstanceOfSchemaCoValuesNullable<T>> : [NonNullable<InstanceOfSchemaCoValuesNullable<T>>] extends [...] ? Exclude<...> extends CoValue ? R extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean ...
export loaded
loaded
<typeof
const BubbleTeaOrder: co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>
BubbleTeaOrder
>;
export const
const DraftBubbleTeaOrder: co.Map<{
    name: z.ZodOptional<z.z.ZodString>;
}, unknown, Account | Group>
DraftBubbleTeaOrder
=
const BubbleTeaOrder: co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>
BubbleTeaOrder
.
CoMapSchema<{ name: ZodString; }, unknown, Account | Group>.partial(): co.Map<{
    name: z.ZodOptional<z.z.ZodString>;
}, unknown, Account | Group>
Creates a new CoMap schema by making all fields optional.
@returnsA new CoMap schema with all fields optional.
partial
();
export type
type DraftBubbleTeaOrder = {
    readonly name: string | undefined;
} & CoMap
DraftBubbleTeaOrder
= import coco.
type loaded<T extends CoValueClassOrSchema, R extends ResolveQuery<T> = true> = R extends boolean | undefined ? NonNullable<InstanceOfSchemaCoValuesNullable<T>> : [NonNullable<InstanceOfSchemaCoValuesNullable<T>>] extends [...] ? Exclude<...> extends CoValue ? R extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean ...
export loaded
loaded
<typeof
const DraftBubbleTeaOrder: co.Map<{
    name: z.ZodOptional<z.z.ZodString>;
}, unknown, Account | Group>
DraftBubbleTeaOrder
>;

Writing the components in React

Let's write the form component that will be used for both create and update.

// OrderForm.tsx
export function 
function OrderForm({ order, onSave, }: {
    order: BubbleTeaOrder | DraftBubbleTeaOrder;
    onSave?: (e: React.FormEvent<HTMLFormElement>) => void;
}): React.JSX.Element
OrderForm
({
order: ({
    readonly name: string;
} & CoMap) | ({
    readonly name: string | undefined;
} & CoMap)
order
,
onSave: ((e: React.FormEvent<HTMLFormElement>) => void) | undefinedonSave, }: {
order: ({
    readonly name: string;
} & CoMap) | ({
    readonly name: string | undefined;
} & CoMap)
order
:
type BubbleTeaOrder = {
    readonly name: string;
} & CoMap
BubbleTeaOrder
|
type DraftBubbleTeaOrder = {
    readonly name: string | undefined;
} & CoMap
DraftBubbleTeaOrder
;
onSave?: ((e: React.FormEvent<HTMLFormElement>) => void) | undefinedonSave?: (e: React.FormEvent<HTMLFormElement>e: React.interface React.FormEvent<T = Element>FormEvent<HTMLFormElement>) => void; }) { return ( <React.JSX.IntrinsicElements.form: React.DetailedHTMLProps<React.FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>form React.DOMAttributes<HTMLFormElement>.onSubmit?: React.FormEventHandler<HTMLFormElement> | undefinedonSubmit={onSave: ((e: React.FormEvent<HTMLFormElement>) => void) | undefinedonSave}> <React.JSX.IntrinsicElements.label: React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>label> Name <React.JSX.IntrinsicElements.input: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>input React.InputHTMLAttributes<HTMLInputElement>.type?: React.HTMLInputTypeAttribute | undefinedtype="text" React.InputHTMLAttributes<HTMLInputElement>.value?: string | number | readonly string[] | undefinedvalue={
order: ({
    readonly name: string;
} & CoMap) | ({
    readonly name: string | undefined;
} & CoMap)
order
.name: string | undefinedname}
React.InputHTMLAttributes<HTMLInputElement>.onChange?: React.ChangeEventHandler<HTMLInputElement> | undefinedonChange={(e: React.ChangeEvent<HTMLInputElement>e) =>
order: ({
    readonly name: string;
} & CoMap) | ({
    readonly name: string | undefined;
} & CoMap)
order
.
CoMap.$jazz: CoMapJazzApi<{
    readonly name: string;
} & CoMap> | CoMapJazzApi<{
    readonly name: string | undefined;
} & CoMap>
Jazz methods for CoMaps are inside this property. This allows CoMaps to be used as plain objects while still having access to Jazz methods, and also doesn't limit which key names can be used inside CoMaps.
$jazz
.CoMapJazzApi<M extends CoMap>.set<"name">(key: "name", value: string): void
Set a value on the CoMap
@paramkey The key to set@paramvalue The value to set@categoryContent
set
("name", e: React.ChangeEvent<HTMLInputElement>e.React.ChangeEvent<HTMLInputElement>.target: EventTarget & HTMLInputElementtarget.HTMLInputElement.value: string
Returns the value of the data at the cursor's current position.
value
)}
React.InputHTMLAttributes<HTMLInputElement>.required?: boolean | undefinedrequired /> </React.JSX.IntrinsicElements.label: React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>label> {onSave: ((e: React.FormEvent<HTMLFormElement>) => void) | undefinedonSave && <React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.ButtonHTMLAttributes<HTMLButtonElement>.type?: "button" | "reset" | "submit" | undefinedtype="submit">Submit</React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button>} </React.JSX.IntrinsicElements.form: React.DetailedHTMLProps<React.FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>form> ); }

Writing the edit form

To make the edit form, simply pass the BubbleTeaOrder.

// EditOrder.tsx
export function 
function EditOrder(props: {
    id: string;
}): React.JSX.Element | undefined
EditOrder
(
props: {
    id: string;
}
props
: { id: stringid: string }) {
const
const order: ({
    readonly name: string;
} & CoMap) | null | undefined
order
=
useCoState<co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>, true>(Schema: co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>, id: string | undefined, options?: {
    ...;
} | undefined): ({
    ...;
} & CoMap) | ... 1 more ... | undefined
React hook for subscribing to CoValues and handling loading states. This hook provides a convenient way to subscribe to CoValues and automatically handles the subscription lifecycle (subscribe on mount, unsubscribe on unmount). It also supports deep loading of nested CoValues through resolve queries.
@returnsThe loaded CoValue, or `undefined` if loading, or `null` if not found/not accessible@example```tsx // Deep loading with resolve queries const Project = co.map({ name: z.string(), tasks: co.list(Task), owner: TeamMember, }); function ProjectView({ projectId }: { projectId: string }) { const project = useCoState(Project, projectId, { resolve: { tasks: { $each: true }, owner: true, }, }); if (!project) { return project === null ? "Project not found or not accessible" : "Loading project..."; } return ( <div> <h1>{project.name}</h1> <p>Owner: {project.owner.name}</p> <ul> {project.tasks.map((task) => ( <li key={task.id}>{task.title}</li> ))} </ul> </div> ); } ```@example```tsx // Using with optional references and error handling const Task = co.map({ title: z.string(), assignee: co.optional(TeamMember), subtasks: co.list(Task), }); function TaskDetail({ taskId }: { taskId: string }) { const task = useCoState(Task, taskId, { resolve: { assignee: true, subtasks: { $each: { $onError: null } }, }, }); if (!task) { return task === null ? "Task not found or not accessible" : "Loading task..."; } return ( <div> <h2>{task.title}</h2> {task.assignee && <p>Assigned to: {task.assignee.name}</p>} <ul> {task.subtasks.map((subtask, index) => ( subtask ? <li key={subtask.id}>{subtask.title}</li> : <li key={index}>Inaccessible subtask</li> ))} </ul> </div> ); } ``` For more examples, see the [subscription and deep loading](https://jazz.tools/docs/react/using-covalues/subscription-and-loading) documentation.
useCoState
(
const BubbleTeaOrder: co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>
BubbleTeaOrder
,
props: {
    id: string;
}
props
.id: stringid);
if (!
const order: ({
    readonly name: string;
} & CoMap) | null | undefined
order
) return;
return <
function OrderForm({ order, onSave, }: {
    order: BubbleTeaOrder | DraftBubbleTeaOrder;
    onSave?: (e: React.FormEvent<HTMLFormElement>) => void;
}): React.JSX.Element
OrderForm
order: ({
    readonly name: string;
} & CoMap) | ({
    readonly name: string | undefined;
} & CoMap)
order
={
const order: {
    readonly name: string;
} & CoMap
order
} />;
}

Writing the create form

For the create form, we need to:

  1. Create a draft order.
  2. Edit the draft order.
  3. Convert the draft order to a "real" order on submit.

Here's how that looks like:

// CreateOrder.tsx
export function function CreateOrder(): React.JSX.Element | undefinedCreateOrder() {
  const { 
const me: NonNullable<Account | ({
    readonly [x: string]: any;
} & Account) | null> | null | undefined
me
} =
useAccount<CoreAccountSchema<z.z.core.$ZodLooseShape> | AccountClass<Account>, true>(AccountSchema?: CoreAccountSchema<z.z.core.$ZodLooseShape> | AccountClass<...> | undefined, options?: {
    ...;
} | undefined): {
    ...;
}
React hook for accessing the current user's account and authentication state. This hook provides access to the current user's account profile and root data, along with authentication utilities. It automatically handles subscription to the user's account data and provides a logout function.
@returnsAn object containing: - `me`: The loaded account data, or `undefined` if loading, or `null` if not authenticated - `agent`: The current agent (anonymous or authenticated user). Can be used as `loadAs` parameter for load and subscribe methods. - `logOut`: Function to log out the current user@example```tsx // Deep loading with resolve queries function ProjectListWithDetails() { const { me } = useAccount(MyAppAccount, { resolve: { profile: true, root: { myProjects: { $each: { tasks: true, }, }, }, }, }); if (!me) { return me === null ? <div>Failed to load your projects</div> : <div>Loading...</div>; } return ( <div> <h1>{me.profile.name}'s projects</h1> <ul> {me.root.myProjects.map((project) => ( <li key={project.id}> {project.name} ({project.tasks.length} tasks) </li> ))} </ul> </div> ); } ```
useAccount
();
const [
const draft: ({
    readonly name: string | undefined;
} & CoMap) | undefined
draft
,
const setDraft: React.Dispatch<React.SetStateAction<({
    readonly name: string | undefined;
} & CoMap) | undefined>>
setDraft
] =
useState<{
    readonly name: string | undefined;
} & CoMap>(): [({
    readonly name: string | undefined;
} & CoMap) | undefined, React.Dispatch<React.SetStateAction<({
    readonly name: string | undefined;
} & CoMap) | undefined>>] (+1 overload)
Returns a stateful value, and a function to update it.
@version16.8.0@see{@link https://react.dev/reference/react/useState}
useState
<
type DraftBubbleTeaOrder = {
    readonly name: string | undefined;
} & CoMap
DraftBubbleTeaOrder
>();
function useEffect(effect: React.EffectCallback, deps?: React.DependencyList): void
Accepts a function that contains imperative, possibly effectful code.
@parameffect Imperative function that can return a cleanup function@paramdeps If present, effect will only activate if the values in the list change.@version16.8.0@see{@link https://react.dev/reference/react/useEffect}
useEffect
(() => {
const setDraft: (value: React.SetStateAction<({
    readonly name: string | undefined;
} & CoMap) | undefined>) => void
setDraft
(
const DraftBubbleTeaOrder: co.Map<{
    name: z.ZodOptional<z.z.ZodString>;
}, unknown, Account | Group>
DraftBubbleTeaOrder
.
CoMapSchema<{ name: ZodOptional<ZodString>; }, unknown, Account | Group>.create(init: {
    name?: string | undefined;
}, options?: {
    owner?: Group;
    unique?: CoValueUniqueness["uniqueness"];
} | Group): {
    ...;
} & CoMap (+1 overload)
create
({}));
}, [
const me: NonNullable<Account | ({
    readonly [x: string]: any;
} & Account) | null> | null | undefined
me
?.
Account.$jazz: AccountJazzApi<Account> | AccountJazzApi<{
    readonly [x: string]: any;
} & Account>
Jazz methods for Accounts are inside this property. This allows Accounts to be used as plain objects while still having access to Jazz methods.
$jazz
.AccountJazzApi<A extends Account>.id: string | undefined
The ID of this `Account`
@categoryContent
id
]);
const const onSave: (e: React.FormEvent<HTMLFormElement>) => voidonSave = (e: React.FormEvent<HTMLFormElement>e: React.interface React.FormEvent<T = Element>FormEvent<HTMLFormElement>) => { e: React.FormEvent<HTMLFormElement>e.React.BaseSyntheticEvent<Event, EventTarget & HTMLFormElement, EventTarget>.preventDefault(): voidpreventDefault(); if (!
const draft: ({
    readonly name: string | undefined;
} & CoMap) | undefined
draft
|| !
const draft: {
    readonly name: string | undefined;
} & CoMap
draft
.name: string | undefinedname) return;
const
const order: {
    readonly name: string;
} & CoMap
order
=
const draft: {
    readonly name: string | undefined;
} & CoMap
draft
as
type BubbleTeaOrder = {
    readonly name: string;
} & CoMap
BubbleTeaOrder
; // TODO: this should narrow correctly
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v20.11.1/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+2 overloads)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)
log
("Order created:",
const order: {
    readonly name: string;
} & CoMap
order
);
}; if (!
const draft: ({
    readonly name: string | undefined;
} & CoMap) | undefined
draft
) return;
return <
function OrderForm({ order, onSave, }: {
    order: co.loaded<typeof BubbleTeaOrder> | co.loaded<typeof DraftBubbleTeaOrder>;
    onSave?: (e: React.FormEvent<HTMLFormElement>) => void;
}): React.JSX.Element
OrderForm
order: ({
    readonly name: string | undefined;
} & CoMap) | ({
    readonly name: string;
} & CoMap)
order
={
const draft: {
    readonly name: string | undefined;
} & CoMap
draft
} onSave?: ((e: React.FormEvent<HTMLFormElement>) => void) | undefinedonSave={const onSave: (e: React.FormEvent<HTMLFormElement>) => voidonSave} />;
}

Validation

In a BubbleTeaOrder, the name field is required, so it would be a good idea to validate this before turning the draft into a real order.

Update the schema to include a validateDraftOrder helper.

// schema.ts
export const 
const BubbleTeaOrder: co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>
BubbleTeaOrder
= import coco.
map<{
    name: z.z.ZodString;
}>(shape: {
    name: z.z.ZodString;
}): co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>
export map
map
({
name: z.z.ZodStringname: import zz.
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString (+1 overload)
export string
string
(),
}); export type
type BubbleTeaOrder = {
    readonly name: string;
} & CoMap
BubbleTeaOrder
= import coco.
type loaded<T extends CoValueClassOrSchema, R extends ResolveQuery<T> = true> = R extends boolean | undefined ? NonNullable<InstanceOfSchemaCoValuesNullable<T>> : [NonNullable<InstanceOfSchemaCoValuesNullable<T>>] extends [...] ? Exclude<...> extends CoValue ? R extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean ...
export loaded
loaded
<typeof
const BubbleTeaOrder: co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>
BubbleTeaOrder
>;
export const
const DraftBubbleTeaOrder: co.Map<{
    name: z.ZodOptional<z.z.ZodString>;
}, unknown, Account | Group>
DraftBubbleTeaOrder
=
const BubbleTeaOrder: co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>
BubbleTeaOrder
.
CoMapSchema<{ name: ZodString; }, unknown, Account | Group>.partial(): co.Map<{
    name: z.ZodOptional<z.z.ZodString>;
}, unknown, Account | Group>
Creates a new CoMap schema by making all fields optional.
@returnsA new CoMap schema with all fields optional.
partial
();
export type
type DraftBubbleTeaOrder = {
    readonly name: string | undefined;
} & CoMap
DraftBubbleTeaOrder
= import coco.
type loaded<T extends CoValueClassOrSchema, R extends ResolveQuery<T> = true> = R extends boolean | undefined ? NonNullable<InstanceOfSchemaCoValuesNullable<T>> : [NonNullable<InstanceOfSchemaCoValuesNullable<T>>] extends [...] ? Exclude<...> extends CoValue ? R extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean ...
export loaded
loaded
<typeof
const DraftBubbleTeaOrder: co.Map<{
    name: z.ZodOptional<z.z.ZodString>;
}, unknown, Account | Group>
DraftBubbleTeaOrder
>;
export function
function validateDraftOrder(draft: DraftBubbleTeaOrder): {
    errors: string[];
}
validateDraftOrder
(
draft: {
    readonly name: string | undefined;
} & CoMap
draft
:
type DraftBubbleTeaOrder = {
    readonly name: string | undefined;
} & CoMap
DraftBubbleTeaOrder
) {
const const errors: string[]errors: string[] = []; if (!
draft: {
    readonly name: string | undefined;
} & CoMap
draft
.name: string | undefinedname) {
const errors: string[]errors.Array<string>.push(...items: string[]): number
Appends new elements to the end of an array, and returns the new length of the array.
@paramitems New elements to add to the array.
push
("Please enter a name.");
} return { errors: string[]errors }; };

Then perform the validation on submit.

// CreateOrder.tsx
export function function CreateOrder(): React.JSX.Element | undefinedCreateOrder() {
  const { 
const me: NonNullable<Account | ({
    readonly [x: string]: any;
} & Account) | null> | null | undefined
me
} =
useAccount<CoreAccountSchema<z.z.core.$ZodLooseShape> | AccountClass<Account>, true>(AccountSchema?: CoreAccountSchema<z.z.core.$ZodLooseShape> | AccountClass<...> | undefined, options?: {
    ...;
} | undefined): {
    ...;
}
React hook for accessing the current user's account and authentication state. This hook provides access to the current user's account profile and root data, along with authentication utilities. It automatically handles subscription to the user's account data and provides a logout function.
@returnsAn object containing: - `me`: The loaded account data, or `undefined` if loading, or `null` if not authenticated - `agent`: The current agent (anonymous or authenticated user). Can be used as `loadAs` parameter for load and subscribe methods. - `logOut`: Function to log out the current user@example```tsx // Deep loading with resolve queries function ProjectListWithDetails() { const { me } = useAccount(MyAppAccount, { resolve: { profile: true, root: { myProjects: { $each: { tasks: true, }, }, }, }, }); if (!me) { return me === null ? <div>Failed to load your projects</div> : <div>Loading...</div>; } return ( <div> <h1>{me.profile.name}'s projects</h1> <ul> {me.root.myProjects.map((project) => ( <li key={project.id}> {project.name} ({project.tasks.length} tasks) </li> ))} </ul> </div> ); } ```
useAccount
();
const [
const draft: ({
    readonly name: string | undefined;
} & CoMap) | undefined
draft
,
const setDraft: React.Dispatch<React.SetStateAction<({
    readonly name: string | undefined;
} & CoMap) | undefined>>
setDraft
] =
useState<{
    readonly name: string | undefined;
} & CoMap>(): [({
    readonly name: string | undefined;
} & CoMap) | undefined, React.Dispatch<React.SetStateAction<({
    readonly name: string | undefined;
} & CoMap) | undefined>>] (+1 overload)
Returns a stateful value, and a function to update it.
@version16.8.0@see{@link https://react.dev/reference/react/useState}
useState
<
type DraftBubbleTeaOrder = {
    readonly name: string | undefined;
} & CoMap
DraftBubbleTeaOrder
>();
function useEffect(effect: React.EffectCallback, deps?: React.DependencyList): void
Accepts a function that contains imperative, possibly effectful code.
@parameffect Imperative function that can return a cleanup function@paramdeps If present, effect will only activate if the values in the list change.@version16.8.0@see{@link https://react.dev/reference/react/useEffect}
useEffect
(() => {
const setDraft: (value: React.SetStateAction<({
    readonly name: string | undefined;
} & CoMap) | undefined>) => void
setDraft
(
const DraftBubbleTeaOrder: co.Map<{
    name: z.ZodOptional<z.z.ZodString>;
}, unknown, Account | Group>
DraftBubbleTeaOrder
.
CoMapSchema<{ name: ZodOptional<ZodString>; }, unknown, Account | Group>.create(init: {
    name?: string | undefined;
}, options?: {
    owner?: Group;
    unique?: CoValueUniqueness["uniqueness"];
} | Group): {
    ...;
} & CoMap (+1 overload)
create
({}));
}, [
const me: NonNullable<Account | ({
    readonly [x: string]: any;
} & Account) | null> | null | undefined
me
?.
Account.$jazz: AccountJazzApi<Account> | AccountJazzApi<{
    readonly [x: string]: any;
} & Account>
Jazz methods for Accounts are inside this property. This allows Accounts to be used as plain objects while still having access to Jazz methods.
$jazz
.AccountJazzApi<A extends Account>.id: string | undefined
The ID of this `Account`
@categoryContent
id
]);
const const onSave: (e: React.FormEvent<HTMLFormElement>) => voidonSave = (e: React.FormEvent<HTMLFormElement>e: React.interface React.FormEvent<T = Element>FormEvent<HTMLFormElement>) => { e: React.FormEvent<HTMLFormElement>e.React.BaseSyntheticEvent<Event, EventTarget & HTMLFormElement, EventTarget>.preventDefault(): voidpreventDefault(); if (!
const draft: ({
    readonly name: string | undefined;
} & CoMap) | undefined
draft
) return;
const
const validation: {
    errors: string[];
}
validation
=
function validateDraftOrder(draft: DraftBubbleTeaOrder): {
    errors: string[];
}
validateDraftOrder
(
const draft: {
    readonly name: string | undefined;
} & CoMap
draft
);
if (
const validation: {
    errors: string[];
}
validation
.errors: string[]errors.Array<T>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
> 0) {
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v20.11.1/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+2 overloads)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)
log
(
const validation: {
    errors: string[];
}
validation
.errors: string[]errors);
return; } const
const order: {
    readonly name: string;
} & CoMap
order
=
const draft: {
    readonly name: string | undefined;
} & CoMap
draft
as
type BubbleTeaOrder = {
    readonly name: string;
} & CoMap
BubbleTeaOrder
;
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v20.11.1/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+2 overloads)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)
log
("Order created:",
const order: {
    readonly name: string;
} & CoMap
order
);
}; if (!
const draft: ({
    readonly name: string | undefined;
} & CoMap) | undefined
draft
) return;
return <
function OrderForm({ order, onSave, }: {
    order: BubbleTeaOrder | DraftBubbleTeaOrder;
    onSave?: (e: React.FormEvent<HTMLFormElement>) => void;
}): React.JSX.Element
OrderForm
order: ({
    readonly name: string | undefined;
} & CoMap) | ({
    readonly name: string;
} & CoMap)
order
={
const draft: {
    readonly name: string | undefined;
} & CoMap
draft
} onSave?: ((e: React.FormEvent<HTMLFormElement>) => void) | undefinedonSave={const onSave: (e: React.FormEvent<HTMLFormElement>) => voidonSave} />;
}

Saving the user's work-in-progress

It turns out that using this pattern also provides a UX improvement.

By storing the draft in the user's account, they can come back to it anytime without losing their work. 🙌

// schema.ts
export const 
const BubbleTeaOrder: co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>
BubbleTeaOrder
= import coco.
map<{
    name: z.z.ZodString;
}>(shape: {
    name: z.z.ZodString;
}): co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>
export map
map
({
name: z.z.ZodStringname: import zz.
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString (+1 overload)
export string
string
(),
}); export type
type BubbleTeaOrder = {
    readonly name: string;
} & CoMap
BubbleTeaOrder
= import coco.
type loaded<T extends CoValueClassOrSchema, R extends ResolveQuery<T> = true> = R extends boolean | undefined ? NonNullable<InstanceOfSchemaCoValuesNullable<T>> : [NonNullable<InstanceOfSchemaCoValuesNullable<T>>] extends [...] ? Exclude<...> extends CoValue ? R extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean ...
export loaded
loaded
<typeof
const BubbleTeaOrder: co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>
BubbleTeaOrder
>;
export const
const DraftBubbleTeaOrder: co.Map<{
    name: z.ZodOptional<z.z.ZodString>;
}, unknown, Account | Group>
DraftBubbleTeaOrder
=
const BubbleTeaOrder: co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>
BubbleTeaOrder
.
CoMapSchema<{ name: ZodString; }, unknown, Account | Group>.partial(): co.Map<{
    name: z.ZodOptional<z.z.ZodString>;
}, unknown, Account | Group>
Creates a new CoMap schema by making all fields optional.
@returnsA new CoMap schema with all fields optional.
partial
();
export type
type DraftBubbleTeaOrder = {
    readonly name: string | undefined;
} & CoMap
DraftBubbleTeaOrder
= import coco.
type loaded<T extends CoValueClassOrSchema, R extends ResolveQuery<T> = true> = R extends boolean | undefined ? NonNullable<InstanceOfSchemaCoValuesNullable<T>> : [NonNullable<InstanceOfSchemaCoValuesNullable<T>>] extends [...] ? Exclude<...> extends CoValue ? R extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean ...
export loaded
loaded
<typeof
const DraftBubbleTeaOrder: co.Map<{
    name: z.ZodOptional<z.z.ZodString>;
}, unknown, Account | Group>
DraftBubbleTeaOrder
>;
export const
const AccountRoot: co.Map<{
    draft: co.Map<{
        name: z.ZodOptional<z.z.ZodString>;
    }, unknown, Account | Group>;
}, unknown, Account | Group>
AccountRoot
= import coco.
map<{
    draft: co.Map<{
        name: z.ZodOptional<z.z.ZodString>;
    }, unknown, Account | Group>;
}>(shape: {
    draft: co.Map<{
        name: z.ZodOptional<z.z.ZodString>;
    }, unknown, Account | Group>;
}): co.Map<...>
export map
map
({
draft: co.Map<{
    name: z.ZodOptional<z.z.ZodString>;
}, unknown, Account | Group>
draft
:
const DraftBubbleTeaOrder: co.Map<{
    name: z.ZodOptional<z.z.ZodString>;
}, unknown, Account | Group>
DraftBubbleTeaOrder
,
}); export const
const JazzAccount: co.Account<{
    root: co.Map<{
        draft: co.Map<{
            name: z.ZodOptional<z.z.ZodString>;
        }, unknown, Account | Group>;
    }, unknown, Account | Group>;
    profile: co.Map<{
        ...;
    }, unknown, Account | Group>;
}>
JazzAccount
= import coco.
account<{
    root: co.Map<{
        draft: co.Map<{
            name: z.ZodOptional<z.z.ZodString>;
        }, unknown, Account | Group>;
    }, unknown, Account | Group>;
    profile: co.Map<...>;
}>(shape?: {
    ...;
} | undefined): co.Account<...>
export account
Defines a collaborative account schema for Jazz applications. Creates an account schema that represents a user account with profile and root data. Accounts are the primary way to identify and manage users in Jazz applications.
@templateShape - The shape of the account schema extending BaseAccountShape@paramshape - The account schema shape. Defaults to a basic profile with name, inbox, and inboxInvite fields, plus an empty root object.@example```typescript // Basic account with default profile const BasicAccount = co.account(); // Custom account with specific profile and root structure const JazzAccount = co.account({ profile: co.profile({ name: z.string(), avatar: z.optional(z.string()), }), root: co.map({ organizations: co.list(Organization), draftOrganization: DraftOrganization, }), }).withMigration(async (account) => { // Migration logic for existing accounts if (!account.$jazz.has("profile")) { const group = Group.create(); account.$jazz.set("profile", co.profile().create( { name: getRandomUsername() }, group )); group.addMember("everyone", "reader"); } }); ```
account
({
root: co.Map<{
    draft: co.Map<{
        name: z.ZodOptional<z.z.ZodString>;
    }, unknown, Account | Group>;
}, unknown, Account | Group>
root
:
const AccountRoot: co.Map<{
    draft: co.Map<{
        name: z.ZodOptional<z.z.ZodString>;
    }, unknown, Account | Group>;
}, unknown, Account | Group>
AccountRoot
,
profile: co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>
profile
: import coco.
map<{
    name: z.z.ZodString;
}>(shape: {
    name: z.z.ZodString;
}): co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>
export map
map
({ name: z.z.ZodStringname: import zz.
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString (+1 overload)
export string
string
() }),
}).
AccountSchema<{ root: CoMapSchema<{ draft: CoMapSchema<{ name: ZodOptional<ZodString>; }, unknown, Account | Group>; }, unknown, Account | Group>; profile: CoMapSchema<...>; }>.withMigration(migration: (account: {
    readonly root: ({
        readonly draft: ({
            readonly name: string | undefined;
        } & CoMap) | null;
    } & CoMap) | null;
    readonly profile: ({
        readonly name: string;
    } & CoMap) | null;
} & Account, creationProps?: {
    name: string;
}) => void): co.Account<...>
withMigration
((
account: {
    readonly root: ({
        readonly draft: ({
            readonly name: string | undefined;
        } & CoMap) | null;
    } & CoMap) | null;
    readonly profile: ({
        readonly name: string;
    } & CoMap) | null;
} & Account
account
,
creationProps: {
    name: string;
} | undefined
creationProps
?: { name: stringname: string }) => {
if (!
account: {
    readonly root: ({
        readonly draft: ({
            readonly name: string | undefined;
        } & CoMap) | null;
    } & CoMap) | null;
    readonly profile: ({
        readonly name: string;
    } & CoMap) | null;
} & Account
account
.
Account.$jazz: AccountJazzApi<{
    readonly root: ({
        readonly draft: ({
            readonly name: string | undefined;
        } & CoMap) | null;
    } & CoMap) | null;
    readonly profile: ({
        readonly name: string;
    } & CoMap) | null;
} & Account>
Jazz methods for Accounts are inside this property. This allows Accounts to be used as plain objects while still having access to Jazz methods.
$jazz
.AccountJazzApi<{ readonly root: ({ readonly draft: ({ readonly name: string | undefined; } & CoMap) | null; } & CoMap) | null; readonly profile: ({ readonly name: string; } & CoMap) | null; } & Account>.has(key: "root" | "profile"): booleanhas("root")) {
account: {
    readonly root: ({
        readonly draft: ({
            readonly name: string | undefined;
        } & CoMap) | null;
    } & CoMap) | null;
    readonly profile: ({
        readonly name: string;
    } & CoMap) | null;
} & Account
account
.
Account.$jazz: AccountJazzApi<{
    readonly root: ({
        readonly draft: ({
            readonly name: string | undefined;
        } & CoMap) | null;
    } & CoMap) | null;
    readonly profile: ({
        readonly name: string;
    } & CoMap) | null;
} & Account>
Jazz methods for Accounts are inside this property. This allows Accounts to be used as plain objects while still having access to Jazz methods.
$jazz
.
AccountJazzApi<{ readonly root: ({ readonly draft: ({ readonly name: string | undefined; } & CoMap) | null; } & CoMap) | null; readonly profile: ({ readonly name: string; } & CoMap) | null; } & Account>.set<"root">(key: "root", value: ({
    readonly draft: ({
        readonly name: string | undefined;
    } & CoMap) | null;
} & CoMap) | CoMapInit<{
    readonly draft: ({
        readonly name: string | undefined;
    } & CoMap) | null;
} & CoMap>): void
Set the value of a key in the account.
@paramkey The key to set.@paramvalue The value to set.@categoryContent
set
("root", { draft: {}draft: {} });
} });

Let's not forget to update the AccountSchema.

import { function JazzReactProvider<S extends (AccountClass<Account> & CoValueFromRaw<Account>) | CoreAccountSchema>({ children, guestMode, sync, storage, AccountSchema, defaultProfileName, onLogOut, logOutReplacement, onAnonymousAccountDiscarded, enableSSR, fallback, }: JazzProviderProps<S>): JSX.Element
@categoryContext & Hooks
JazzReactProvider
} from "jazz-tools/react";
import {
const JazzAccount: AccountSchema<{
    root: CoMapSchema<{
        draft: CoMapSchema<{
            name: ZodOptional<ZodString>;
        }, unknown, Account | Group>;
    }, unknown, Account | Group>;
    profile: CoMapSchema<...>;
}>
JazzAccount
} from "./schema";
export function
function MyJazzProvider({ children }: {
    children: React.ReactNode;
}): React.JSX.Element
MyJazzProvider
({ children: React.ReactNodechildren }: { children: React.ReactNodechildren: React.type React.ReactNode = string | number | bigint | boolean | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | React.ReactPortal | Promise<...> | null | undefined
Represents all of the things React can render. Where {@link ReactElement } only represents JSX, `ReactNode` represents everything that can be rendered.
@see{@link https://react-typescript-cheatsheet.netlify.app/docs/react-types/reactnode/ React TypeScript Cheatsheet}@example```tsx // Typing children type Props = { children: ReactNode } const Component = ({ children }: Props) => <div>{children}</div> <Component>hello</Component> ```@example```tsx // Typing a custom element type Props = { customElement: ReactNode } const Component = ({ customElement }: Props) => <div>{customElement}</div> <Component customElement={<div>hello</div>} /> ```
ReactNode
}) {
return ( <function JazzReactProvider<S extends (AccountClass<Account> & CoValueFromRaw<Account>) | CoreAccountSchema>({ children, guestMode, sync, storage, AccountSchema, defaultProfileName, onLogOut, logOutReplacement, onAnonymousAccountDiscarded, enableSSR, fallback, }: JazzProviderProps<S>): JSX.Element
@categoryContext & Hooks
JazzReactProvider
sync: SyncConfigsync={{ peer: "wss://cloud.jazz.tools/?key=you@example.com"peer: "wss://cloud.jazz.tools/?key=you@example.com" }}
AccountSchema?: AccountSchema<{
    root: CoMapSchema<{
        draft: CoMapSchema<{
            name: ZodOptional<ZodString>;
        }, unknown, Account | Group>;
    }, unknown, Account | Group>;
    profile: CoMapSchema<...>;
}> | undefined
AccountSchema
={
const JazzAccount: AccountSchema<{
    root: CoMapSchema<{
        draft: CoMapSchema<{
            name: ZodOptional<ZodString>;
        }, unknown, Account | Group>;
    }, unknown, Account | Group>;
    profile: CoMapSchema<...>;
}>
JazzAccount
}
> {children: React.ReactNodechildren} </function JazzReactProvider<S extends (AccountClass<Account> & CoValueFromRaw<Account>) | CoreAccountSchema>({ children, guestMode, sync, storage, AccountSchema, defaultProfileName, onLogOut, logOutReplacement, onAnonymousAccountDiscarded, enableSSR, fallback, }: JazzProviderProps<S>): JSX.Element
@categoryContext & Hooks
JazzReactProvider
>
); }

Instead of creating a new draft every time we use the create form, let's use the draft from the account root.

// CreateOrder.tsx
export function function CreateOrder(): React.JSX.Element | undefinedCreateOrder() {
  const { 
const me: CoMapLikeLoaded<{
    readonly root: ({
        readonly draft: ({
            readonly name: string | undefined;
        } & CoMap) | null;
    } & CoMap) | null;
    readonly profile: ({
        readonly name: string;
    } & CoMap) | null;
} & Account, {
    ...;
}, 10, []> | null | undefined
me
} =
useAccount<co.Account<{
    root: co.Map<{
        draft: co.Map<{
            name: ZodOptional<ZodString>;
        }, unknown, Account | Group>;
    }, unknown, Account | Group>;
    profile: co.Map<...>;
}>, {
    ...;
}>(AccountSchema?: co.Account<...> | undefined, options?: {
    ...;
} | undefined): {
    ...;
}
React hook for accessing the current user's account and authentication state. This hook provides access to the current user's account profile and root data, along with authentication utilities. It automatically handles subscription to the user's account data and provides a logout function.
@returnsAn object containing: - `me`: The loaded account data, or `undefined` if loading, or `null` if not authenticated - `agent`: The current agent (anonymous or authenticated user). Can be used as `loadAs` parameter for load and subscribe methods. - `logOut`: Function to log out the current user@example```tsx // Deep loading with resolve queries function ProjectListWithDetails() { const { me } = useAccount(MyAppAccount, { resolve: { profile: true, root: { myProjects: { $each: { tasks: true, }, }, }, }, }); if (!me) { return me === null ? <div>Failed to load your projects</div> : <div>Loading...</div>; } return ( <div> <h1>{me.profile.name}'s projects</h1> <ul> {me.root.myProjects.map((project) => ( <li key={project.id}> {project.name} ({project.tasks.length} tasks) </li> ))} </ul> </div> ); } ```
useAccount
(
const JazzAccount: co.Account<{
    root: co.Map<{
        draft: co.Map<{
            name: ZodOptional<ZodString>;
        }, unknown, Account | Group>;
    }, unknown, Account | Group>;
    profile: co.Map<...>;
}>
JazzAccount
, {
resolve?: RefsToResolve<{
    readonly root: ({
        readonly draft: ({
            readonly name: string | undefined;
        } & CoMap) | null;
    } & CoMap) | null;
    readonly profile: ({
        readonly name: string;
    } & CoMap) | null;
} & Account, 10, []> | undefined
Resolve query to specify which nested CoValues to load from the account
resolve
: {
root?: RefsToResolve<{
    readonly draft: ({
        readonly name: string | undefined;
    } & CoMap) | null;
} & CoMap, 10, [0]> | undefined
root
: {
draft?: RefsToResolve<{
    readonly name: string | undefined;
} & CoMap, 10, [0, 0]> | undefined
draft
: true } },
}); if (!
const me: CoMapLikeLoaded<{
    readonly root: ({
        readonly draft: ({
            readonly name: string | undefined;
        } & CoMap) | null;
    } & CoMap) | null;
    readonly profile: ({
        readonly name: string;
    } & CoMap) | null;
} & Account, {
    ...;
}, 10, []> | null | undefined
me
?.
Account.root: ({
    readonly draft: {
        readonly name: string | undefined;
    } & CoMap;
} & {
    readonly draft: ({
        readonly name: string | undefined;
    } & CoMap) | null;
} & CoMap) | undefined
root
) return;
const const onSave: (e: React.FormEvent<HTMLFormElement>) => voidonSave = (e: React.FormEvent<HTMLFormElement>e: React.interface React.FormEvent<T = Element>FormEvent<HTMLFormElement>) => { e: React.FormEvent<HTMLFormElement>e.React.BaseSyntheticEvent<Event, EventTarget & HTMLFormElement, EventTarget>.preventDefault(): voidpreventDefault(); const
const draft: {
    readonly name: string | undefined;
} & CoMap
draft
=
const me: CoMapLikeLoaded<{
    readonly root: ({
        readonly draft: ({
            readonly name: string | undefined;
        } & CoMap) | null;
    } & CoMap) | null;
    readonly profile: ({
        readonly name: string;
    } & CoMap) | null;
} & Account, {
    ...;
}, 10, []>
me
.
Account.root: {
    readonly draft: {
        readonly name: string | undefined;
    } & CoMap;
} & {
    readonly draft: ({
        readonly name: string | undefined;
    } & CoMap) | null;
} & CoMap
root
.
draft: {
    readonly name: string | undefined;
} & CoMap
draft
;
if (!
const draft: {
    readonly name: string | undefined;
} & CoMap
draft
) return;
const
const validation: {
    errors: string[];
}
validation
=
function validateDraftOrder(draft: DraftBubbleTeaOrder): {
    errors: string[];
}
validateDraftOrder
(
const draft: {
    readonly name: string | undefined;
} & CoMap
draft
);
if (
const validation: {
    errors: string[];
}
validation
.errors: string[]errors.Array<T>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
> 0) {
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v20.11.1/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+2 overloads)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)
log
(
const validation: {
    errors: string[];
}
validation
.errors: string[]errors);
return; } const
const order: {
    readonly name: string;
} & CoMap
order
=
const draft: {
    readonly name: string | undefined;
} & CoMap
draft
as
type BubbleTeaOrder = {
    readonly name: string;
} & CoMap
BubbleTeaOrder
;
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v20.11.1/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+2 overloads)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)
log
("Order created:",
const order: {
    readonly name: string;
} & CoMap
order
);
// create a new empty draft
const me: CoMapLikeLoaded<{
    readonly root: ({
        readonly draft: ({
            readonly name: string | undefined;
        } & CoMap) | null;
    } & CoMap) | null;
    readonly profile: ({
        readonly name: string;
    } & CoMap) | null;
} & Account, {
    ...;
}, 10, []>
me
.
Account.root: {
    readonly draft: {
        readonly name: string | undefined;
    } & CoMap;
} & {
    readonly draft: ({
        readonly name: string | undefined;
    } & CoMap) | null;
} & CoMap
root
.
CoMap.$jazz: CoMapJazzApi<{
    readonly draft: {
        readonly name: string | undefined;
    } & CoMap;
} & {
    readonly draft: ({
        readonly name: string | undefined;
    } & CoMap) | null;
} & CoMap>
Jazz methods for CoMaps are inside this property. This allows CoMaps to be used as plain objects while still having access to Jazz methods, and also doesn't limit which key names can be used inside CoMaps.
$jazz
.
CoMapJazzApi<{ readonly draft: { readonly name: string | undefined; } & CoMap; } & { readonly draft: ({ readonly name: string | undefined; } & CoMap) | null; } & CoMap>.set<"draft">(key: "draft", value: ({
    readonly name: string | undefined;
} & CoMap) | CoMapInit<{
    readonly name: string | undefined;
} & CoMap>): void
Set a value on the CoMap
@paramkey The key to set@paramvalue The value to set@categoryContent
set
("draft", {});
}; return <
function CreateOrderForm({ id, onSave, }: {
    id: string;
    onSave: (e: React.FormEvent<HTMLFormElement>) => void;
}): React.JSX.Element | undefined
CreateOrderForm
id: stringid={
const me: CoMapLikeLoaded<{
    readonly root: ({
        readonly draft: ({
            readonly name: string | undefined;
        } & CoMap) | null;
    } & CoMap) | null;
    readonly profile: ({
        readonly name: string;
    } & CoMap) | null;
} & Account, {
    ...;
}, 10, []>
me
.
Account.root: {
    readonly draft: {
        readonly name: string | undefined;
    } & CoMap;
} & {
    readonly draft: ({
        readonly name: string | undefined;
    } & CoMap) | null;
} & CoMap
root
.
draft: {
    readonly name: string | undefined;
} & CoMap
draft
.
CoMap.$jazz: CoMapJazzApi<{
    readonly name: string | undefined;
} & CoMap>
Jazz methods for CoMaps are inside this property. This allows CoMaps to be used as plain objects while still having access to Jazz methods, and also doesn't limit which key names can be used inside CoMaps.
$jazz
.CoMapJazzApi<M extends CoMap>.id: string
The ID of this `CoMap`
@categoryContent
id
} onSave: (e: React.FormEvent<HTMLFormElement>) => voidonSave={const onSave: (e: React.FormEvent<HTMLFormElement>) => voidonSave} />
} function
function CreateOrderForm({ id, onSave, }: {
    id: string;
    onSave: (e: React.FormEvent<HTMLFormElement>) => void;
}): React.JSX.Element | undefined
CreateOrderForm
({
id: stringid, onSave: (e: React.FormEvent<HTMLFormElement>) => voidonSave, }: { id: stringid: string onSave: (e: React.FormEvent<HTMLFormElement>) => voidonSave: (e: React.FormEvent<HTMLFormElement>e: React.interface React.FormEvent<T = Element>FormEvent<HTMLFormElement>) => void; }) { const
const draft: ({
    readonly name: string | undefined;
} & CoMap) | null | undefined
draft
=
useCoState<co.Map<{
    name: ZodOptional<ZodString>;
}, unknown, Account | Group>, true>(Schema: co.Map<{
    name: ZodOptional<ZodString>;
}, unknown, Account | Group>, id: string | undefined, options?: {
    ...;
} | undefined): ({
    ...;
} & CoMap) | ... 1 more ... | undefined
React hook for subscribing to CoValues and handling loading states. This hook provides a convenient way to subscribe to CoValues and automatically handles the subscription lifecycle (subscribe on mount, unsubscribe on unmount). It also supports deep loading of nested CoValues through resolve queries.
@returnsThe loaded CoValue, or `undefined` if loading, or `null` if not found/not accessible@example```tsx // Deep loading with resolve queries const Project = co.map({ name: z.string(), tasks: co.list(Task), owner: TeamMember, }); function ProjectView({ projectId }: { projectId: string }) { const project = useCoState(Project, projectId, { resolve: { tasks: { $each: true }, owner: true, }, }); if (!project) { return project === null ? "Project not found or not accessible" : "Loading project..."; } return ( <div> <h1>{project.name}</h1> <p>Owner: {project.owner.name}</p> <ul> {project.tasks.map((task) => ( <li key={task.id}>{task.title}</li> ))} </ul> </div> ); } ```@example```tsx // Using with optional references and error handling const Task = co.map({ title: z.string(), assignee: co.optional(TeamMember), subtasks: co.list(Task), }); function TaskDetail({ taskId }: { taskId: string }) { const task = useCoState(Task, taskId, { resolve: { assignee: true, subtasks: { $each: { $onError: null } }, }, }); if (!task) { return task === null ? "Task not found or not accessible" : "Loading task..."; } return ( <div> <h2>{task.title}</h2> {task.assignee && <p>Assigned to: {task.assignee.name}</p>} <ul> {task.subtasks.map((subtask, index) => ( subtask ? <li key={subtask.id}>{subtask.title}</li> : <li key={index}>Inaccessible subtask</li> ))} </ul> </div> ); } ``` For more examples, see the [subscription and deep loading](https://jazz.tools/docs/react/using-covalues/subscription-and-loading) documentation.
useCoState
(
const DraftBubbleTeaOrder: co.Map<{
    name: ZodOptional<ZodString>;
}, unknown, Account | Group>
DraftBubbleTeaOrder
, id: stringid);
if (!
const draft: ({
    readonly name: string | undefined;
} & CoMap) | null | undefined
draft
) return;
return <
function OrderForm({ order, onSave, }: {
    order: BubbleTeaOrder | DraftBubbleTeaOrder;
    onSave?: (e: React.FormEvent<HTMLFormElement>) => void;
}): React.JSX.Element
OrderForm
order: ({
    readonly name: string | undefined;
} & CoMap) | ({
    readonly name: string;
} & CoMap)
order
={
const draft: {
    readonly name: string | undefined;
} & CoMap
draft
} onSave?: ((e: React.FormEvent<HTMLFormElement>) => void) | undefinedonSave={onSave: (e: React.FormEvent<HTMLFormElement>) => voidonSave} />;
}

When the new draft is created, we need to call useCoState again, so that we are passing the new draft to <OrderForm/>.

There you have it! Notice that when you refresh the page, you will see your unsaved changes.

Draft indicator

To improve the UX even further, in just a few more steps, we can tell the user that they currently have unsaved changes.

Simply add a hasChanges helper to your schema.

// schema.ts
export const 
const BubbleTeaOrder: co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>
BubbleTeaOrder
= import coco.
map<{
    name: z.z.ZodString;
}>(shape: {
    name: z.z.ZodString;
}): co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>
export map
map
({
name: z.z.ZodStringname: import zz.
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString (+1 overload)
export string
string
(),
}); export type
type BubbleTeaOrder = {
    readonly name: string;
} & CoMap
BubbleTeaOrder
= import coco.
type loaded<T extends CoValueClassOrSchema, R extends ResolveQuery<T> = true> = R extends boolean | undefined ? NonNullable<InstanceOfSchemaCoValuesNullable<T>> : [NonNullable<InstanceOfSchemaCoValuesNullable<T>>] extends [...] ? Exclude<...> extends CoValue ? R extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean ...
export loaded
loaded
<typeof
const BubbleTeaOrder: co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>
BubbleTeaOrder
>;
export const
const DraftBubbleTeaOrder: co.Map<{
    name: z.ZodOptional<z.z.ZodString>;
}, unknown, Account | Group>
DraftBubbleTeaOrder
=
const BubbleTeaOrder: co.Map<{
    name: z.z.ZodString;
}, unknown, Account | Group>
BubbleTeaOrder
.
CoMapSchema<{ name: ZodString; }, unknown, Account | Group>.partial(): co.Map<{
    name: z.ZodOptional<z.z.ZodString>;
}, unknown, Account | Group>
Creates a new CoMap schema by making all fields optional.
@returnsA new CoMap schema with all fields optional.
partial
();
export type
type DraftBubbleTeaOrder = {
    readonly name: string | undefined;
} & CoMap
DraftBubbleTeaOrder
= import coco.
type loaded<T extends CoValueClassOrSchema, R extends ResolveQuery<T> = true> = R extends boolean | undefined ? NonNullable<InstanceOfSchemaCoValuesNullable<T>> : [NonNullable<InstanceOfSchemaCoValuesNullable<T>>] extends [...] ? Exclude<...> extends CoValue ? R extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends {
    ...;
} ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean ...
export loaded
loaded
<typeof
const DraftBubbleTeaOrder: co.Map<{
    name: z.ZodOptional<z.z.ZodString>;
}, unknown, Account | Group>
DraftBubbleTeaOrder
>;
export function
function validateDraftOrder(draft: DraftBubbleTeaOrder): {
    errors: string[];
}
validateDraftOrder
(
draft: {
    readonly name: string | undefined;
} & CoMap
draft
:
type DraftBubbleTeaOrder = {
    readonly name: string | undefined;
} & CoMap
DraftBubbleTeaOrder
) {
const const errors: string[]errors: string[] = []; if (!
draft: {
    readonly name: string | undefined;
} & CoMap
draft
.name: string | undefinedname) {
const errors: string[]errors.Array<string>.push(...items: string[]): number
Appends new elements to the end of an array, and returns the new length of the array.
@paramitems New elements to add to the array.
push
("Please enter a name.");
} return { errors: string[]errors }; }; export function function hasChanges(draft?: DraftBubbleTeaOrder): number | falsehasChanges(
draft: ({
    readonly name: string | undefined;
} & CoMap) | undefined
draft
?:
type DraftBubbleTeaOrder = {
    readonly name: string | undefined;
} & CoMap
DraftBubbleTeaOrder
) {
return
draft: ({
    readonly name: string | undefined;
} & CoMap) | undefined
draft
? var Object: ObjectConstructor
Provides functionality common to all JavaScript objects.
Object
.ObjectConstructor.keys(o: {}): string[] (+1 overload)
Returns the names of the enumerable string properties and methods of an object.
@paramo Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
keys
(
draft: {
    readonly name: string | undefined;
} & CoMap
draft
.
CoMap.$jazz: CoMapJazzApi<{
    readonly name: string | undefined;
} & CoMap>
Jazz methods for CoMaps are inside this property. This allows CoMaps to be used as plain objects while still having access to Jazz methods, and also doesn't limit which key names can be used inside CoMaps.
$jazz
.
CoMapJazzApi<{ readonly name: string | undefined; } & CoMap>.getEdits(): CoMapEdits<{
    readonly name: string | undefined;
} & CoMap>
Get the edits made to the CoMap.
@categoryCollaboration
getEdits
()).Array<T>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
: false;
};

In the UI, you can choose how you want to show the draft indicator.

// DraftIndicator.tsx
export function function DraftIndicator(): React.JSX.Element | undefinedDraftIndicator() {
  const { 
const me: CoMapLikeLoaded<{
    readonly root: ({
        readonly draft: ({
            readonly name: string | undefined;
        } & CoMap) | null;
    } & CoMap) | null;
    readonly profile: ({
        readonly name: string;
    } & CoMap) | null;
} & Account, {
    ...;
}, 10, []> | null | undefined
me
} =
useAccount<co.Account<{
    root: co.Map<{
        draft: co.Map<{
            name: z.ZodOptional<z.z.ZodString>;
        }, unknown, Account | Group>;
    }, unknown, Account | Group>;
    profile: co.Map<...>;
}>, {
    ...;
}>(AccountSchema?: co.Account<...> | undefined, options?: {
    ...;
} | undefined): {
    ...;
}
React hook for accessing the current user's account and authentication state. This hook provides access to the current user's account profile and root data, along with authentication utilities. It automatically handles subscription to the user's account data and provides a logout function.
@returnsAn object containing: - `me`: The loaded account data, or `undefined` if loading, or `null` if not authenticated - `agent`: The current agent (anonymous or authenticated user). Can be used as `loadAs` parameter for load and subscribe methods. - `logOut`: Function to log out the current user@example```tsx // Deep loading with resolve queries function ProjectListWithDetails() { const { me } = useAccount(MyAppAccount, { resolve: { profile: true, root: { myProjects: { $each: { tasks: true, }, }, }, }, }); if (!me) { return me === null ? <div>Failed to load your projects</div> : <div>Loading...</div>; } return ( <div> <h1>{me.profile.name}'s projects</h1> <ul> {me.root.myProjects.map((project) => ( <li key={project.id}> {project.name} ({project.tasks.length} tasks) </li> ))} </ul> </div> ); } ```
useAccount
(
const JazzAccount: co.Account<{
    root: co.Map<{
        draft: co.Map<{
            name: z.ZodOptional<z.z.ZodString>;
        }, unknown, Account | Group>;
    }, unknown, Account | Group>;
    profile: co.Map<{
        ...;
    }, unknown, Account | Group>;
}>
JazzAccount
, {
resolve?: RefsToResolve<{
    readonly root: ({
        readonly draft: ({
            readonly name: string | undefined;
        } & CoMap) | null;
    } & CoMap) | null;
    readonly profile: ({
        readonly name: string;
    } & CoMap) | null;
} & Account, 10, []> | undefined
Resolve query to specify which nested CoValues to load from the account
resolve
: {
root?: RefsToResolve<{
    readonly draft: ({
        readonly name: string | undefined;
    } & CoMap) | null;
} & CoMap, 10, [0]> | undefined
root
: {
draft?: RefsToResolve<{
    readonly name: string | undefined;
} & CoMap, 10, [0, 0]> | undefined
draft
: true } },
}); if (function hasChanges(draft?: DraftBubbleTeaOrder): number | falsehasChanges(
const me: CoMapLikeLoaded<{
    readonly root: ({
        readonly draft: ({
            readonly name: string | undefined;
        } & CoMap) | null;
    } & CoMap) | null;
    readonly profile: ({
        readonly name: string;
    } & CoMap) | null;
} & Account, {
    ...;
}, 10, []> | null | undefined
me
?.
Account.root: {
    readonly draft: {
        readonly name: string | undefined;
    } & CoMap;
} & {
    readonly draft: ({
        readonly name: string | undefined;
    } & CoMap) | null;
} & CoMap
root
.
draft: ({
    readonly name: string | undefined;
} & CoMap) | undefined
draft
)) {
return ( <React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>You have a draft</React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p> ); } }

A more subtle way is to show a small dot next to the Create button.

Handling different types of data

Forms can be more complex than just a single string field, so we've put together an example app that shows you how to handle single-select, multi-select, date, and boolean inputs.

See the full example here.

// schema.ts
export const 
const BubbleTeaOrder: co.Map<{
    baseTea: z.z.ZodLiteral<"Black" | "Oolong" | "Jasmine" | "Thai">;
    addOns: co.List<z.z.ZodLiteral<"Pearl" | "Lychee jelly" | "Red bean" | "Brown sugar" | "Taro">>;
    deliveryDate: z.z.ZodDate;
    withMilk: z.z.ZodBoolean;
    instructions: z.ZodOptional<...>;
}, unknown, Account | Group>
BubbleTeaOrder
= import coco.
map<{
    baseTea: z.z.ZodLiteral<"Black" | "Oolong" | "Jasmine" | "Thai">;
    addOns: co.List<z.z.ZodLiteral<"Pearl" | "Lychee jelly" | "Red bean" | "Brown sugar" | "Taro">>;
    deliveryDate: z.z.ZodDate;
    withMilk: z.z.ZodBoolean;
    instructions: z.ZodOptional<...>;
}>(shape: {
    ...;
}): co.Map<...>
export map
map
({
baseTea: z.z.ZodLiteral<"Black" | "Oolong" | "Jasmine" | "Thai">baseTea: import zz.
literal<readonly ["Black", "Oolong", "Jasmine", "Thai"]>(value: readonly ["Black", "Oolong", "Jasmine", "Thai"], params?: string | z.z.core.$ZodLiteralParams): z.z.ZodLiteral<"Black" | "Oolong" | "Jasmine" | "Thai"> (+1 overload)
export literal
literal
(["Black", "Oolong", "Jasmine", "Thai"]),
addOns: co.List<z.z.ZodLiteral<"Pearl" | "Lychee jelly" | "Red bean" | "Brown sugar" | "Taro">>addOns: const ListOfBubbleTeaAddOns: co.List<z.z.ZodLiteral<"Pearl" | "Lychee jelly" | "Red bean" | "Brown sugar" | "Taro">>ListOfBubbleTeaAddOns, deliveryDate: z.z.ZodDatedeliveryDate: import zz.
function date(params?: string | z.z.core.$ZodDateParams): z.z.ZodDate
export date
date
(),
withMilk: z.z.ZodBooleanwithMilk: import zz.
function boolean(params?: string | z.z.core.$ZodBooleanParams): z.z.ZodBoolean
export boolean
boolean
(),
instructions: z.ZodOptional<z.z.ZodString>instructions: import zz.
optional<z.z.ZodString>(innerType: z.z.ZodString): z.ZodOptional<z.z.ZodString>
export optional
optional
(import zz.
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString (+1 overload)
export string
string
()),
});