Skip to content

Next.js Setup

This guide covers setting up SyncRabbit in a Next.js application with automatic engine management.

Installation

bash
npm install @syncrabbit/nextjs

Setup

1. Environment Variables

Create or update your .env file:

bash
DATABASE_URL=postgresql://user:password@host:5432/database

2. Initialize Database

Run the init command to create the replication slot (one-time setup):

bash
npx syncrabbit init

3. Start Engine on Boot

Create instrumentation.ts in your project root:

typescript
export async function register() {
  if (process.env.NEXT_RUNTIME === "nodejs") {
    const { registerSyncRabbit } = await import("@syncrabbit/nextjs/server");
    await registerSyncRabbit();
  }
}

4. Add Provider

Create a client component wrapper:

tsx
// components/SyncRabbitRootProvider.tsx
"use client";

import { SyncRabbitProvider } from "@syncrabbit/nextjs";

export function SyncRabbitRootProvider({
  children,
}: {
  children: React.ReactNode;
}) {
  return <SyncRabbitProvider>{children}</SyncRabbitProvider>;
}

Wrap your app in app/layout.tsx:

tsx
import { SyncRabbitRootProvider } from "@/components/SyncRabbitRootProvider";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html>
      <body>
        <SyncRabbitRootProvider>{children}</SyncRabbitRootProvider>
      </body>
    </html>
  );
}

Usage

useTable - Subscribe to a Table

tsx
"use client";

import { useTable } from "@syncrabbit/nextjs";

interface Todo {
  id: number;
  title: string;
  completed: boolean;
}

export default function TodoList() {
  const { data: todos, loading, error } = useTable<Todo>("todos", {
    initialFetch: async () => {
      const res = await fetch("/api/todos");
      return res.json();
    },
  });

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
}

useWatch - React to Changes

tsx
"use client";

import { useWatch } from "@syncrabbit/nextjs";
import { useQueryClient } from "@tanstack/react-query";

export function OrderWatcher() {
  const queryClient = useQueryClient();

  useWatch(["orders", "order_items"], () => {
    queryClient.invalidateQueries({ queryKey: ["orders"] });
  });

  return null;
}

useMutation - Write with Instant Invalidation

tsx
"use client";

import { useMutation } from "@syncrabbit/nextjs";

export function CreateTodo() {
  const { mutate, loading } = useMutation({
    tables: ["todos"],
    mutationFn: async (data: { title: string }) => {
      const res = await fetch("/api/todos", {
        method: "POST",
        body: JSON.stringify(data),
      });
      return res.json();
    },
  });

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    mutate({ title: formData.get("title") as string });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="title" placeholder="New todo" />
      <button type="submit" disabled={loading}>
        Add
      </button>
    </form>
  );
}

Released under the MIT License.