Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.abloatai.com/llms.txt

Use this file to discover all available pages before exploring further.

A production-shaped Next.js + Ablo Sync app. App Router, Server Actions, React Server Components, and live client subscriptions.

Structure

app/
  tasks/
    [id]/
      page.tsx          # RSC: retrieve + render
      actions.ts        # Server Action: schema update with stale-state check
      TaskEditor.tsx    # Client: live updates
  lib/
    ablo.ts             # Schema-backed Ablo client for server actions

RSC Initial Render

// app/tasks/[id]/page.tsx
import { ablo } from '@/lib/ablo';

export default async function TaskPage({
  params,
}: { params: { id: string } }) {
  await ablo.ready();
  const [task] = await ablo.tasks.load({ where: { id: params.id } });
  if (!task) return null;

  return <TaskEditor task={task} />;
}

Server Action Commit

// app/tasks/[id]/actions.ts
'use server';

import { ablo } from '@/lib/ablo';

export async function markDone(id: string) {
  const busy = ablo.intents.list({ resource: 'tasks', id });
  if (busy.length > 0) return { status: 'busy', intents: busy };

  const snap = ablo.snapshot({ tasks: id });
  const task = await ablo.tasks.update(
    id,
    { status: 'done' },
    { readAt: snap.stamp, onStale: 'reject', wait: 'confirmed' },
  );

  return { status: 'done', task };
}
If another participant commits between the read and the write, the commit rejects. The action can re-fetch and ask the user to retry.

Live Client

'use client';

import { useAblo } from '@abloatai/ablo/react';

export function TaskEditor({ task: serverTask }: Props) {
  const data = useAblo((ablo) => ablo.tasks.retrieve(serverTask.id)) ?? serverTask;
  const intents = useAblo((ablo) =>
    ablo.intents.list({ resource: 'tasks', id: serverTask.id }),
  ) ?? [];
  const busy = intents.length > 0;

  return (
    <button disabled={busy || data.status === 'done'}>
      {busy ? 'Someone is editing' : 'Mark done'}
    </button>
  );
}

More