Instant Code Examples

Each example is a self-contained Instant app that you can copy and paste into your own projects.

To get rolling, create a free account, grab your app ID, and install @instantdb/react.

Psst... this is a realtime page! 🔥

We created a full-fledged Instant app just for you. Share this page's unique URL with your friends, and you'll see them in the previews below!

URL
Loading...

Please note: this app will automatically expire and be deleted in 2 weeks.

Todos

2 previews
import { id, init, tx } from '@instantdb/react';

const db = init<{
  todos: {
    id: string;
    text: string;
    completed: boolean;
  };
}>({
  appId: "__YOUR_APP_ID__",
});

export default function InstantTodos() {
  const { data, isLoading, error } = db.useQuery({
    todos: {},
  });

  if (error)
    return <p className="p-4 flex items-center">Oops, something broke</p>;

  return (
    <div className="flex flex-col p-4 gap-2">
      <h1 className="font-bold text-lg">InsTodo</h1>
      <form
        className="flex flex-col gap-2"
        onSubmit={(e) => {
          e.preventDefault();
          const form = e.currentTarget;
          const todoInput = form.elements.namedItem('todo') as HTMLInputElement;
          const text = todoInput?.value;

          if (!text) return;

          form.reset();

          db.transact([
            tx.todos[id()].update({
              text,
              completed: false,
            }),
          ]);
        }}
      >
        <input className="py-1 border-gray-300" type="text" name="todo" />
        <button type="submit" className="bg-blue-500 text-white p-1 font-bold">
          Add todo
        </button>
      </form>
      {isLoading ? (
        <p className="italic text-gray-700">Loading...</p>
      ) : data?.todos.length ? (
        <ul>
          {data.todos.map((todo) => (
            <li
              key={todo.id}
              className="flex items-center justify-between gap-2"
            >
              <label className="truncate">
                <input
                  type="checkbox"
                  className="align-middle"
                  checked={todo.completed}
                  onChange={(e) => {
                    db.transact([
                      tx.todos[todo.id].update({
                        completed: e.currentTarget.checked,
                      }),
                    ]);
                  }}
                />{' '}
                <span
                  className={`align-middle ${
                    todo.completed ? 'line-through text-gray-400' : ''
                  }`}
                >
                  {todo.text}
                </span>
              </label>
              <button
                onClick={(e) => {
                  db.transact([tx.todos[todo.id].delete()]);
                }}
              >
                ×
              </button>
            </li>
          ))}
        </ul>
      ) : (
        <p className="italic text-gray-700">No todos!</p>
      )}
    </div>
  );
}

Auth

2 previews
import { init } from '@instantdb/react';
import { useState } from 'react';

const db = init({
  appId: "__YOUR_APP_ID__",
});

export default function InstantAuth() {
  const { isLoading, user, error } = db.useAuth();

  if (isLoading) {
    return <div className={cls.root}>Loading...</div>;
  }

  if (error) {
    return <div className={cls.root}>Uh oh! {error.message}</div>;
  }

  if (user) {
    return <div className={cls.root}>Hello {user.email}!</div>;
  }

  return <Login />;
}

function Login() {
  const [state, setState] = useState({
    sentEmail: '',
    email: '',
    error: null,
    code: '',
  });

  const { sentEmail, email, code, error } = state;

  if (!sentEmail) {
    return (
      <form
        className={cls.root}
        onSubmit={async (e) => {
          e.preventDefault();

          if (!email) return;

          setState({ ...state, sentEmail: email, error: null });

          try {
            await db.auth.sendMagicCode({ email });
          } catch (error: any) {
            setState({ ...state, error: error.body?.message });
          }
        }}
      >
        <h2 className={cls.heading}>Let's log you in!</h2>
        <input
          className={cls.input}
          placeholder="Enter your email"
          type="email"
          value={email}
          onChange={(e) =>
            setState({ ...state, email: e.target.value, error: null })
          }
        />
        <button type="submit" className={cls.button}>
          Send Code
        </button>
        {error ? <p className={cls.error}>{error}</p> : null}
      </form>
    );
  }

  return (
    <form
      className={cls.root}
      onSubmit={async (e) => {
        e.preventDefault();

        if (!code) return;

        try {
          await db.auth.signInWithMagicCode({ email: sentEmail, code });
        } catch (error: any) {
          setState({ ...state, error: error.body?.message });
        }
      }}
    >
      <h2 className={cls.heading}>
        Okay we sent you an email! What was the code?
      </h2>
      <input
        className={cls.input}
        type="text"
        placeholder="Magic code"
        value={code || ''}
        onChange={(e) =>
          setState({ ...state, code: e.target.value, error: null })
        }
      />
      <button className={cls.button}>Verify</button>
      {error ? <p className={cls.error}>{error}</p> : null}
    </form>
  );
}

const cls = {
  root: 'flex max-w-xs mx-auto flex-col gap-3 items-center h-screen px-2 pt-12',
  heading: 'text-lg font-bold',
  input: 'py-1 border-gray-300 rounded w-full',
  button: 'bg-blue-500 text-white px-3 py-1 rounded w-full',
  error: 'text-red-700 text-sm bg-red-50 border-red-500 border p-2',
};

Cursors

2 previews
import { Cursors, init } from '@instantdb/react';

const db = init<
  {},
  {
    'cursors-example': {};
  }
>({
  appId: "__YOUR_APP_ID__",
});

const room = db.room('cursors-example', '123');

export default function InstantCursors() {
  return (
    <Cursors
      room={room}
      userCursorColor={randomColor}
      className={cursorsClassNames}
    >
      Move your cursor around! ✨
    </Cursors>
  );
}

const randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16);

const cursorsClassNames =
  'flex h-screen w-screen items-center justify-center overflow-hidden font-mono text-sm text-gray-800';

Custom cursors

2 previews
import { Cursors, init } from '@instantdb/react';

const db = init<
  {},
  {
    'cursors-example': {
      presence: {
        name: string;
      };
    };
  }
>({
  appId: "__YOUR_APP_ID__",
});

const room = db.room('cursors-example', '124');

function CustomCursor({ color, name }: { color?: string; name: string }) {
  return (
    <span
      className="rounded-b-xl rounded-r-xl border-2 bg-white/30 px-3 text-xs shadow-lg backdrop-blur-md"
      style={{
        borderColor: color ?? 'gray',
      }}
    >
      {name}
    </span>
  );
}

export default function InstantCursors() {
  room.useSyncPresence({
    name: userId,
  });

  return (
    <Cursors
      room={room}
      renderCursor={(props) => (
        <CustomCursor color={props.color} name={props.presence.name} />
      )}
      userCursorColor={randomColor}
      className={cursorsClassNames}
    >
      Move your cursor around! ✨
    </Cursors>
  );
}

const userId = Math.random().toString(36).slice(2, 6);

const randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16);

const cursorsClassNames =
  'flex h-screen w-screen items-center justify-center overflow-hidden font-mono text-sm text-gray-800';

Reactions

2 previews
import { init } from '@instantdb/react';
import { RefObject, createRef, useRef } from 'react';

const db = init<{}, RoomSchema>({
  appId: "__YOUR_APP_ID__",
});

const { usePublishTopic, useTopicEffect } = db.room('topics-example', '123');

export default function InstantTopics() {
  const publishEmoji = usePublishTopic('emoji');

  useTopicEffect('emoji', ({ name, directionAngle, rotationAngle }) => {
    if (!emoji[name]) return;

    animateEmoji(
      { emoji: emoji[name], directionAngle, rotationAngle },
      elRefsRef.current[name].current
    );
  });

  const elRefsRef = useRef<{
    [k: string]: RefObject<HTMLDivElement>;
  }>(refsInit);

  return (
    <div className={containerClassNames}>
      <div className="flex gap-4">
        {emojiNames.map((name) => (
          <div className="relative" key={name} ref={elRefsRef.current[name]}>
            <button
              className={emojiButtonClassNames}
              onClick={() => {
                const params = {
                  name,
                  rotationAngle: Math.random() * 360,
                  directionAngle: Math.random() * 360,
                };
                animateEmoji(
                  {
                    emoji: emoji[name],
                    rotationAngle: params.rotationAngle,
                    directionAngle: params.directionAngle,
                  },
                  elRefsRef.current[name].current
                );

                publishEmoji(params);
              }}
            >
              {emoji[name]}
            </button>
          </div>
        ))}
      </div>
    </div>
  );
}

type EmojiName = keyof typeof emoji;

type RoomSchema = {
  'topics-example': {
    topics: {
      emoji: {
        name: EmojiName;
        rotationAngle: number;
        directionAngle: number;
      };
    };
  };
};

const emoji = {
  fire: '🔥',
  wave: '👋',
  confetti: '🎉',
  heart: '❤️',
} as const;

const emojiNames = Object.keys(emoji) as EmojiName[];

const refsInit = Object.fromEntries(
  emojiNames.map((name) => [name, createRef<HTMLDivElement>()])
);

const containerClassNames =
  'flex h-screen w-screen items-center justify-center overflow-hidden bg-gray-200 select-none';

const emojiButtonClassNames =
  'rounded-lg bg-white p-3 text-3xl shadow-lg transition duration-200 ease-in-out hover:-translate-y-1 hover:shadow-xl';

function animateEmoji(
  config: { emoji: string; directionAngle: number; rotationAngle: number },
  target: HTMLDivElement | null
) {
  if (!target) return;

  const rootEl = document.createElement('div');
  const directionEl = document.createElement('div');
  const spinEl = document.createElement('div');

  spinEl.innerText = config.emoji;
  directionEl.appendChild(spinEl);
  rootEl.appendChild(directionEl);
  target.appendChild(rootEl);

  style(rootEl, {
    transform: `rotate(${config.directionAngle * 360}deg)`,
    position: 'absolute',
    top: '0',
    left: '0',
    right: '0',
    bottom: '0',
    margin: 'auto',
    zIndex: '9999',
    pointerEvents: 'none',
  });

  style(spinEl, {
    transform: `rotateZ(${config.rotationAngle * 400}deg)`,
    fontSize: `40px`,
  });

  setTimeout(() => {
    style(directionEl, {
      transform: `translateY(40vh) scale(2)`,
      transition: 'all 400ms',
      opacity: '0',
    });
  }, 20);

  setTimeout(() => rootEl.remove(), 800);
}

function style(el: HTMLElement, styles: Partial<CSSStyleDeclaration>) {
  Object.assign(el.style, styles);
}

Typing indicator

2 previews
import { init } from '@instantdb/react';

const db = init<
  {},
  {
    'typing-indicator-example': {
      presence: {
        id: string;
        name: string;
        color: string;
      };
    };
  }
>({
  appId: "__YOUR_APP_ID__",
});

const userId = Math.random().toString(36).slice(2, 6);
const randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16);
const user = {
  id: userId,
  name: `${userId}`,
  color: randomColor,
};

const room = db.room('typing-indicator-example', '1234');

export default function InstantTypingIndicator() {
  room.useSyncPresence(user);

  const presence = room.usePresence();

  const { active, inputProps } = room.useTypingIndicator('chat');

  const peers = Object.values(presence.peers).filter((p) => p.id);
  const activeMap = Object.fromEntries(
    active.map((activePeer) => [activePeer.id, activePeer])
  );

  return (
    <div className="flex h-screen gap-3 p-2">
      <div className="flex w-10 flex-col gap-2" key="side">
        {peers.map((peer) => {
          return (
            <div
              key={peer.id}
              className="relative inset-0 flex h-10 w-10 items-center justify-center rounded-full border-4 bg-white"
              style={{
                borderColor: peer.color,
              }}
            >
              {peer.name?.slice(0, 1)}
              {activeMap[peer.id] ? (
                <div className="absolute -right-1 bottom-0 rounded-sm bg-black px-1 leading-3 text-white shadow">

                </div>
              ) : null}
            </div>
          );
        })}
      </div>
      <div key="main" className="flex flex-1 flex-col justify-end">
        <textarea
          placeholder="Compose your message here..."
          className="w-full rounded-md border-gray-300 p-2 text-sm"
          onKeyDown={(e) => inputProps.onKeyDown(e)}
          onBlur={() => inputProps.onBlur()}
        />
        <div className="truncate text-xs text-gray-500">
          {active.length ? typingInfo(active) : <>&nbsp;</>}
        </div>
      </div>
    </div>
  );
}

function typingInfo(typing: { name: string }[]) {
  if (typing.length === 0) return null;
  if (typing.length === 1) return `${typing[0].name} is typing...`;
  if (typing.length === 2)
    return `${typing[0].name} and ${typing[1].name} are typing...`;

  return `${typing[0].name} and ${typing.length - 1} others are typing...`;
}

Avatar stack

2 previews
import { init } from '@instantdb/react';

const db = init<
  {},
  {
    'avatars-example': {
      presence: {
        name: string;
        color: string;
      };
    };
  }
>({
  appId: "__YOUR_APP_ID__",
});

const room = db.room('avatars-example', 'avatars-example-1234');

const userId = Math.random().toString(36).slice(2, 6);
const randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16);

export default function InstantAvatarStack() {
  room.useSyncPresence({
    name: userId,
    color: randomColor,
  });

  const presence = room.usePresence();

  return (
    <div className="flex h-screen justify-center items-center">
      {presence.user ? (
        <Avatar
          key={'user'}
          name={presence.user.name}
          color={presence.user.color}
        />
      ) : null}
      {Object.entries(presence.peers).map(([id, peer]) => (
        <Avatar key={id} name={peer.name} color={peer.color} />
      ))}
    </div>
  );
}

function Avatar({ name, color }: { name: string; color: string }) {
  return (
    <div
      key={'user'}
      className={avatarClassNames}
      style={{
        borderColor: color,
      }}
    >
      {name?.slice(0, 1)}
      <div className="hidden group-hover:flex absolute z-10 bottom-10 text-sm text-gray-800 bg-gray-200 rounded px-2">
        {name}
      </div>
    </div>
  );
}

const avatarClassNames =
  'group relative select-none h-10 w-10 bg-gray-50 border border-4 border-black user-select rounded-full first:ml-0 flex justify-center items-center -ml-2 first:ml-0 relative';

Instant
Engineered in New York & San Francisco