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

@doync/react

v0.4.0

Published

doync React adapter: thin useQuery / useQueryOnce / useLocalQuery / useMutation hooks over the client call surface

Downloads

1,199

Readme

@doync/react

Thin React hooks over the doync client call surface (subscribe / once / local / mutate). The hooks consume any DoyncClient — construct one with @doync/web or @doync/mobile and pass it to the Provider. Environment packages are never re-exported from here.

Apps import client types (DoyncClient, View, …) from this package; @doync/client is for adapter authors.

Install

pnpm add @doync/react @doync/client @doync/core
# plus one environment package:
pnpm add @doync/web      # browser
# or
pnpm add @doync/mobile   # React Native

Peer: react ≥ 18.

Provider

Boot the environment client once, then provide it to the tree. Keep the same client object for the life of the app (or tab); drive login/logout through client.updateAuth, not by reconstructing the client.

import { DoyncProvider } from '@doync/react'
import { createWebClient } from '@doync/web'
// or: import { createMobileClient } from '@doync/mobile'

const client = createWebClient({/* worker, authData, … — see @doync/web */})

export function App() {
  return (
    <DoyncProvider client={client}>
      <TaskList />
    </DoyncProvider>
  )
}

useDoyncClient() returns the same client (throws outside a Provider) when you need the imperative surface from a component.

Shared definition module

Hooks take the same queries / mutations trees every client uses — define them once through createDoync / createDoyncDrizzle and import that module from web, mobile, and the DB-worker entry:

// shared/data.ts — one data layer for every client
export { schema, queries, mutations }

Query-taking hooks accept a Bound query: call the registered leaf with its args, queries.issues.open(args). Binding is pure; a fresh Bound query object per render does not bust memoization (key is name + args). Falsy (cond && queries.issues.open(args)) means "no query" and keeps stable hook order.

useQuery

Subscribe to a registered query. Mount retains the shared View handle; unmount releases it. Rows update live as pokes and optimistic writes move them. The second tuple element is the View status.

import { useQuery } from '@doync/react'
import { queries } from './shared/data'

function IssueList({ projectId }: { projectId: string }) {
  const [issues, status] = useQuery(queries.issues.open({ projectId }))
  // multi-row: issues is readonly Row[]
  // a sql.one / findFirst query types as Row | undefined instead

  if (status.status !== 'complete' && issues.length === 0) {
    return <Spinner />
  }
  return (
    <ul>
      {issues.map((issue) => (
        <li key={issue.id}>{issue.title}</li>
      ))}
    </ul>
  )
}

Options (second argument):

  • ttl — connected-clock grace for this Subscription.
  • skip: true — short-circuit the desire while keeping shape-preserving empties ([] multi-row / undefined one-query). Distinct from a falsy query argument, which yields undefined rows and status 'unknown' for both shapes.

One-ness is inferred from the bound query (sql.one / Drizzle findFirst) — never passed as an option.

useQueryOnce

Cache-and-network Once read. Rows render from the local Replica immediately, then update when the server answer lands. status tracks the network half.

const [rows, { status }] = useQueryOnce(queries.issueById({ id }))
// status: 'loading' | 'success' | 'error' | 'skipped' (falsy argument)

useLocalQuery

Arbitrary SQL over the synced Replica — aggregates, joins, window functions. Re-runs on local commits; never registered upstream; offline-capable and free to the server.

const [rows, status] = useLocalQuery<{ n: number }>(
  'select count(*) as n from issue where open = 1',
)
// or: useLocalQuery({ sql: 'select … where id = ?', params: [id] })
// or: useLocalQuery(() => ({ sql, params }))

A falsy source skips (undefined rows, status 'unknown').

useMutation

A registered mutation as a callable. Apply optimistically and push; render off client, await authoritative confirmation off server when it matters.

import { useMutation } from '@doync/react'
import { mutations } from './shared/data'

function CreateIssue() {
  const createIssue = useMutation(mutations.issue.create)

  async function onSubmit(input: { id: string; title: string }) {
    const { client, server } = createIssue(input)
    await client // local apply settled (throws if the local body rejects)
    // optional: await server  // Mirror confirmed (throws if rejected)
  }

  return /* … */
}

Connection and schema status

import { useConnectionStatus, useSchemaStatus } from '@doync/react'

function StatusPill() {
  const connection = useConnectionStatus()
  // 'connecting' | 'connected' | 'disconnected' | 'error' | 'needs-auth'

  const schema = useSchemaStatus()
  // null when nominal; else { kind: 'reload' | 'server-behind' | 'resync' | 'forget', message }
  // Re-reads on both entry AND silent clear — a banner driven only by a
  // one-shot callback would stick after recovery.

  return (
    <>
      <span>{connection}</span>
      {schema ? <Banner>{schema.message}</Banner> : null}
    </>
  )
}

Public surface

| Export | Role | | --- | --- | | DoyncProvider / DoyncProviderProps | Provide the client | | useDoyncClient | Imperative client from context | | useQuery / UseQueryOptions | Live Subscription | | useQueryOnce / OnceStatus | Cache-and-network Once | | useLocalQuery / LocalSource | Local SQL over the Replica | | useMutation | Optimistic mutate + push | | useConnectionStatus | Mirror socket health | | useSchemaStatus | Schema-handling state (or null) | | BoundQuery | Re-exported from @doync/core | | DoyncClient / View / OnceView / ViewStatus / QueryStatus / ConnectionStatus / SchemaEvent / SchemaEventKind / FalsyQuery / MutationOptions / MutationResult / LogoutBehavior / SubscribeOptions / PreloadOptions / PreloadHandle / WarmupOptions / WarmupHandle | Client call-surface family (re-exported from @doync/client) |

Internal API

The main entry (.) is the semver-governed public surface documented here. Anything imported from @doync/react/internal may change in any release, including patches, without notice — use it only if you accept that risk.