npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dittolive/react-ditto

v1.0.1

Published

React wrappers for Ditto

Readme

React Wrapper Library for Ditto

TypeScript example workflow

This is a React wrapper library for Ditto.

Currently, this project works in web browsers, Node.JS and Electron environments.

If you have questions about this project or require any assistance, please get in touch via [email protected].

Ditto SDK v5

This library targets the Ditto v5 SDK (@dittolive/ditto@^5.0.0) and uses DQL (Ditto Query Language) exclusively. The v4 query-builder hooks (usePendingCursorOperation, useMutations, etc.) and the identity hooks (useOnlinePlaygroundIdentity, etc.) have been removed. See the Ditto v5 migration guide for the underlying SDK changes.

Installation

Install this library alongside the Ditto SDK with npm or yarn:

npm install @dittolive/ditto @dittolive/react-ditto
yarn add @dittolive/ditto @dittolive/react-ditto

At the top level of your React app, wrap it with one of the context providers, DittoProvider or DittoLazyProvider:

<DittoProvider setup={createDittoInstance}>
  {({ loading, error }) => {
    if (loading) return <span>Loading Ditto...</span>
    if (error)
      return (
        <span>There was an error loading Ditto. Error: {error.toString()}</span>
      )
    return <App />
  }}
</DittoProvider>

At this point you're ready to build your Ditto app using the hooks provided by the library. Read on to learn how to choose between DittoProvider and DittoLazyProvider, how to open Ditto instances with the v5 API, and how to use the useQuery and useExecuteQuery hooks.

Choosing a Provider

The library includes two context providers that initialize the Ditto context for your app: DittoProvider and DittoLazyProvider.

DittoProvider should be used when the set of Ditto instances used by your app is known beforehand and can be created when the app is bootstrapped. In all other cases, DittoLazyProvider can be used to lazily create Ditto instances on demand when a child of the provider first requires one.

The API for both providers is identical except for the setup prop, which returns Ditto | Ditto[] | Promise<Ditto | Ditto[]> for DittoProvider and Promise<Ditto | null> for DittoLazyProvider.

Choosing one provider over the other has no effect on the remaining hooks — they are used in exactly the same way regardless of provider.

DittoProvider Example

import { Ditto, DittoConfig } from '@dittolive/ditto'

const createDittoInstance = async () => {
  const ditto = await Ditto.open(
    new DittoConfig('my-database-id', { mode: 'smallPeersOnly' }, 'some-path'),
  )
  ditto.sync.start()
  return ditto
}

;<DittoProvider setup={createDittoInstance}>
  {({ loading, error }) => {
    if (loading) return <span>Loading Ditto...</span>
    if (error)
      return (
        <span>There was an error loading Ditto. Error: {error.toString()}</span>
      )
    return <App />
  }}
</DittoProvider>

DittoLazyProvider Example

import { Ditto, DittoConfig } from '@dittolive/ditto'

const createDittoInstance = async (path: string) => {
  const ditto = await Ditto.open(
    new DittoConfig('my-database-id', { mode: 'smallPeersOnly' }, path),
  )
  ditto.sync.start()
  return ditto
}

;<DittoLazyProvider setup={createDittoInstance}>
  {({ loading, error }) => {
    if (loading) return <span>Loading Ditto...</span>
    if (error)
      return (
        <span>There was an error loading Ditto. Error: {error.toString()}</span>
      )
    return <App />
  }}
</DittoLazyProvider>

When multiple instances are registered, hooks select one via the persistenceDirectory option, which matches the persistence directory you set on the instance's DittoConfig. When omitted, the first registered instance is used.

Opening Ditto instances

In v5, a Ditto instance is opened with Ditto.open(config) (async) or Ditto.openSync(config), where the config is a DittoConfig describing the database ID, the connection mode, and an optional persistence directory.

Offline / peer-to-peer

import { Ditto, DittoConfig } from '@dittolive/ditto'

const ditto = await Ditto.open(
  new DittoConfig('my-database-id', { mode: 'smallPeersOnly' }, '/path'),
)
ditto.sync.start()

Online (connected to a Ditto server / Big Peer)

Server connections require an authentication expiration handler to be set before sync is started — ditto.sync.start() throws otherwise. Copy the whole connection URL from the Ditto portal (Connect via SDK → URL); do not build it from the database ID.

import { Authenticator, Ditto, DittoConfig } from '@dittolive/ditto'

const ditto = await Ditto.open(
  new DittoConfig(
    'my-database-id',
    { mode: 'server', url: 'REPLACE_ME_WITH_YOUR_URL' },
    '/path',
  ),
)

await ditto.auth.setExpirationHandler(async (ditto) => {
  await ditto.auth.login('your-token', Authenticator.DEVELOPMENT_PROVIDER)
})

ditto.sync.start()

Querying with useQuery

useQuery continuously fetches the results of a non-mutating DQL query by registering a store observer and (unless localOnly is set) a sync subscription. It returns the current items, loading state, and any error.

import { useQuery } from '@dittolive/react-ditto'

type Task = { _id: string; body: string; isCompleted: boolean }

export default function App() {
  const { items, isLoading, error } = useQuery<Task>(
    'SELECT * FROM tasks WHERE isCompleted = :isCompleted',
    { queryArguments: { isCompleted: false } },
  )

  if (isLoading) return <p>Loading…</p>
  if (error) return <p>Error: {String(error)}</p>

  return (
    <ul>
      {items.map((task) => (
        <li key={task.value._id}>{task.value.body}</li>
      ))}
    </ul>
  )
}

Mutating and on-demand queries with useExecuteQuery

useExecuteQuery returns an execution function that runs a DQL query on demand. Use it for mutations (insert/update/delete) and ad-hoc queries triggered by user actions. It does not set up a sync subscription, so pair mutations with a useQuery for the same collection if you want them synced and observed.

import { useExecuteQuery } from '@dittolive/react-ditto'

export default function AddTask() {
  const [insertTask] = useExecuteQuery<void, { value: { body: string } }>(
    'INSERT INTO tasks DOCUMENTS (:value)',
  )

  return (
    <button onClick={() => insertTask({ value: { body: 'Buy milk' } })}>
      Add Task
    </button>
  )
}

Query arguments can be supplied when setting up the hook and/or when calling the execution function; when both are provided they are shallow-merged, with the execution function's arguments taking precedence.

Quick Start with vite

  1. Install the library:
npm install @dittolive/ditto @dittolive/react-ditto
  1. In ./src/index.tsx, set up Ditto with the DittoProvider:
// ... other imports from vite above
import { Ditto, DittoConfig } from '@dittolive/ditto'
import { DittoProvider } from '@dittolive/react-ditto'

/**
 * This configuration is optional for web browser-based React applications. It
 * tells the `DittoProvider` where to load the .wasm file. If no path is provided
 * (ie. initOptions is undefined), the wasm is loaded from our CDN. If you enable
 * this, make sure to serve `ditto.wasm` with the correct MIME type and CORS
 * headers. See
 * https://www.npmjs.com/package/@dittolive/ditto#browser-environments for
 * details.
 **/
const initOptions = {
  webAssemblyModule: '/ditto.wasm',
}

const RootComponent = () => (
  <DittoProvider
    setup={async () => {
      // Create a database at https://portal.ditto.live/ and copy its database
      // ID and connection URL from the settings page.
      const ditto = await Ditto.open(
        new DittoConfig(
          'your-database-id',
          { mode: 'smallPeersOnly' },
          'testing',
        ),
      )
      ditto.sync.start()
      return ditto
    }}
    /* initOptions={initOptions} */
  >
    {({ loading, error }) => {
      if (loading) return <p>Loading</p>
      if (error) return <p>{error.message}</p>
      return <App />
    }}
  </DittoProvider>
)

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  <React.StrictMode>
    <RootComponent />
  </React.StrictMode>,
)
  1. In your App component, use useQuery to observe documents and useExecuteQuery to mutate them — see the examples above and the full vite example app.

Working with online apps

Using the Portal you can create apps that sync with a Ditto server (Big Peer). Configure the instance with a { mode: 'server', url } connection and authenticate through ditto.auth:

  • Set an expiration handler with ditto.auth.setExpirationHandler(handler). The handler receives the Ditto instance and is invoked whenever (re)authentication is required.
  • Inside the handler, call ditto.auth.login(token, provider). Use Authenticator.DEVELOPMENT_PROVIDER for playground/development tokens, or your own provider string when you have configured authentication webhooks on the Portal.
  • The expiration handler must be set before calling ditto.sync.start() for server connections.

The current authentication state is available via ditto.auth.status ({ isAuthenticated, userID }).

Building this library and running tests

  • Building: run npm run build.
  • Type-check: run npm run types.
  • Lint: run npm run lint.
  • Run tests: run npm test. The suite runs in headless Chrome via Karma and exercises the real Ditto WASM build.

Running example apps

Each example project is in its own directory underneath the ./examples directory.