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
.
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!
Loading...
Please note: this app will automatically expire and be deleted in 2 weeks.
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>
);
}
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',
};
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={randomDarkColor}
className={cursorsClassNames}
>
Move your cursor around! ✨
</Cursors>
);
}
const randomDarkColor = '#' + [0, 0, 0].map(() => Math.floor(Math.random() * 200).toString(16).padStart(2, '0')).join('');
const cursorsClassNames =
'flex h-screen w-screen items-center justify-center overflow-hidden font-mono text-sm text-gray-800';
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={randomDarkColor}
className={cursorsClassNames}
>
Move your cursor around! ✨
</Cursors>
);
}
const userId = Math.random().toString(36).slice(2, 6);
const randomDarkColor = '#' + [0, 0, 0].map(() => Math.floor(Math.random() * 200).toString(16).padStart(2, '0')).join('');
const cursorsClassNames =
'flex h-screen w-screen items-center justify-center overflow-hidden font-mono text-sm text-gray-800';
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);
}
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 randomDarkColor = '#' + [0, 0, 0].map(() => Math.floor(Math.random() * 200).toString(16).padStart(2, '0')).join('');
const user = {
id: userId,
name: `${userId}`,
color: randomDarkColor,
};
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) : <> </>}
</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...`;
}
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 randomDarkColor = '#' + [0, 0, 0].map(() => Math.floor(Math.random() * 200).toString(16).padStart(2, '0')).join('');
export default function InstantAvatarStack() {
room.useSyncPresence({
name: userId,
color: randomDarkColor,
});
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';