decentrl.
SDK

React Hooks

React bindings for Decentrl using useSyncExternalStore for tear-free reads of encrypted state.

@decentrl/sdk-react provides React hooks that connect your components to the Decentrl client. Built on useSyncExternalStore for tear-free concurrent reads.

Installation

npm install @decentrl/sdk-react

Provider Setup

Wrap your app with DecentrlProvider:

import { DecentrlProvider } from '@decentrl/sdk-react'

function App() {
  return (
    <DecentrlProvider client={client}>
      <Chat />
    </DecentrlProvider>
  )
}

Core Hooks

useDecentrl()

Returns the publish method and the client instance:

import { useDecentrl } from '@decentrl/sdk-react'

function SendButton() {
  const { publish, client } = useDecentrl()

  const send = () => publish('message', {
    text: 'Hello',
    threadId: crypto.randomUUID(),
  })

  return <button onClick={send}>Send</button>
}

useDecentrlState(selector?)

Subscribes to client state with an optional selector function:

import { useDecentrlState } from '@decentrl/sdk-react'

// Full state
const state = useDecentrlState()

// With selector — only re-renders when messages change
const messages = useDecentrlState(s => s.messages)
const count = useDecentrlState(s => s.messages.length)

useDecentrlIdentity()

Manages identity state and operations:

import { useDecentrlIdentity } from '@decentrl/sdk-react'

function IdentitySetup() {
  const { identity, isInitialized, create, contracts } = useDecentrlIdentity()

  if (!isInitialized) {
    return (
      <button onClick={() => create({
        alias: 'alice',
        mediatorDid: 'did:web:mediator.decentrl.io',
      })}>
        Create Identity
      </button>
    )
  }

  return <p>DID: {identity.did}</p>
}

useAutoSync(options?)

Starts automatic background sync. Cleans up on unmount:

import { useAutoSync } from '@decentrl/sdk-react'

function App() {
  useAutoSync({
    intervalMs: 3000,
    websocket: true,
    onError: (err) => console.error(err),
  })

  return <Chat />
}

useConnectionStatus()

const status = useConnectionStatus()
// 'disconnected' | 'connecting' | 'authenticating' | 'connected'

Contract Hooks

import {
  useActiveContracts,
  usePendingContracts,
  useContractByDid,
  useRequestContract,
  useAcceptContract,
} from '@decentrl/sdk-react'

// List active contracts
const { contracts, refresh, isRefreshing } = useActiveContracts()

// List pending inbound requests
const { data: pending, refetch } = usePendingContracts()

// Find contract by DID
const contract = useContractByDid(bobDid)

// Request a contract (mutation hook)
const { mutate: requestContract, isPending } = useRequestContract()
requestContract({ recipientDid: bobDid })

// Accept a contract (mutation hook)
const { mutate: acceptContract } = useAcceptContract()
acceptContract({
  pendingId: request.id,
  encryptedPayload: request.encryptedPayload,
  requestorEphemeralPublicKey: request.requestorEphemeralPublicKey,
})

Public Event Hooks

usePublishPublic()

Mutation hook for publishing public events:

import { usePublishPublic } from '@decentrl/sdk-react'

function PublishButton() {
  const { mutate: publishPublic, isPending } = usePublishPublic()

  return (
    <button
      disabled={isPending}
      onClick={() => publishPublic(['blog.post', { title: 'Hello', body: 'World' }])}
    >
      Publish
    </button>
  )
}

usePublicEventQuery(options)

Query public events from any publisher with pagination:

import { usePublicEventQuery } from '@decentrl/sdk-react'

function PublicFeed({ publisherDid }: { publisherDid: string }) {
  const { data, isLoading, hasMore, fetchMore, refetch } = usePublicEventQuery({
    publisherDid,
    channelId: 'blog',
    tags: ['featured'],
    pageSize: 10,
  })

  if (isLoading) return <p>Loading...</p>

  return (
    <div>
      {data.map((event) => (
        <article key={event.id}>
          <p>{JSON.parse(event.event).data.title}</p>
          <small>{new Date(event.timestamp * 1000).toLocaleString()}</small>
        </article>
      ))}
      {hasMore && <button onClick={fetchMore}>Load more</button>}
    </div>
  )
}
OptionTypeRequiredDescription
publisherDidstringYesDID of the publisher to query
channelIdstringNoFilter by channel
tagsstring[]NoFilter by tags (OR semantics)
pageSizenumberNoResults per page (default 20)
enabledbooleanNoDisable the query (default true)

Event Hooks

useEventStream(callback, options?)

Subscribe to real-time events with optional filtering:

import { useEventStream } from '@decentrl/sdk-react'

useEventStream(
  (event) => console.log('New:', event.type, event.data),
  { types: ['message'] },
)

useEventQuery(options)

Query historical events with pagination and state selection:

import { useEventQuery } from '@decentrl/sdk-react'

const { data: messages, isLoading, hasMore, fetchMore } = useEventQuery({
  tags: ['thread.abc'],
  select: (state) => state.messages,
  pageSize: 20,
})