Pick your app

The examples below will be updated with your app ID.

Platform features

Strong Init (experimental)

What if you could use the types you defined in instant.schema.ts, inside init? We have a new version of init out that lets you do this. It's in beta, but if you use it, useQuery and transact will automatically get typesafety and intellisense.

Here's how it works

If you want to migrate, swap out init with init_experimental and pass it your app's graph schema object.

import { init_experimental } from '@instantdb/react';
// 1. Import your schema file
// Don't have a schema? check out https://www.instantdb.com/docs/cli to get started
import schema from '../instant.schema.ts';

const db = init_experimental({
  appId: '__APP_ID__',
  // 2. Use it inside `init_experimental`
  schema,
});

export default function App() {
  // 3. now useQuery is strongly typed!
  const todosRes = db.useQuery({
    todos: {},
  });

  function addTodo({ title: string }) {
    db.transact([
      // 4. mutations are typechecked too!
      db.tx.todos[id()].update({ title }),
    ]);
  }

  return <TodoApp todosRes={todosRes} onAddTodo={addTodo} />;
}

Changes to tx

If you used Instant before, you would use the global tx object. Instead, use the schema-aware db.tx.

// ✅ use `db.tx`
db.tx.todos[id()].update({ done: true }); // note the `db`

Changes to useQuery

Once you switch to init_experimental, your query results get more powerful too.

Previously, all responses in a useQuery returned arrays. Now, we can use your schema to decide. If you have a 'has one' relationship, we can return just one item directly.

Since the new useQuery is schema-aware, we know when to return a single item instead of an array. 🎉 Bear in mind that if you're migrating from init, you'll need to update all of your call sites that reference these "has-one" relationships.

const { data } = useQuery({ users: { author: {} } });
const firstUser = data.users[0];

// before
const author = firstUser.author[0];

// after
const author = firstUser.author; // no more array! 🎉

If you don't want to migrate your components just yet, you can opt out by adding a cardinalityInference flag set to false in your init_experimental call. This way, you'll get all the typechecking benefits without having to update your logic.

Rooms support

Rooms are still expressed in pure TypeScript. You can type rooms with schema.withRoomSchema<R>. Here's how it looks:

type RoomSchema = {
  room: {
    presence: {
      example: number;
    };
  };
};

const db = init_experimental({
  appId: '__APP_ID__',
  schema: schema.withRoomSchema<RoomSchema>(),
});

Reusable types

Sometimes, you'll want to abstract out your query and result types. For example, a query's result might be consumed across multiple React components, each with their own prop types. For such cases, we provide InstantQuery and InstantQueryResult.

To declare a query and validate it's type against your schema, you can import InstantQuery and leverage TypeScript's satisfies operator like so: const myQuery = { myTable: {} } satisfies InstantQuery<DB>;.

To obtain the resolved result type of your query, import InstantQueryResult and provide it your DB and query types const myQueryResult = InstantQueryResult<DB, typeof myQuery>.

If you only want the type of a single entity, you can leverage InstantEntity. InstantEntity resolves an entity type from your DB: type Todo = InstantEntity<DB, 'todos'>. You can specify links relative to the entity, too: type Todo = InstantEntity<DB, 'todos', { category: {}, assignee: {} }>

Here's a full example demonstranting reusable query types in a React app.