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

@texaryn/react

v0.6.0

Published

React bindings, hooks, prop getters, and default widgets for Texaryn

Readme

@texaryn/react

React bindings, hooks, prop getters, renderer components, and default widgets for Texaryn.

Status: pre-1.0. Public APIs may change before 1.0.

Install

pnpm add @texaryn/core @texaryn/schema-json @texaryn/react react react-dom

@texaryn/react requires React 18 or newer.

Quick start

import { createRoot } from 'react-dom/client'
import { createJsonSchemaAdapter } from '@texaryn/schema-json'
import {
  FormProvider,
  FormRoot,
  createDefaultRegistry,
  useForm,
} from '@texaryn/react'

const schema = {
  $schema: 'https://json-schema.org/draft/2020-12/schema',
  type: 'object',
  title: 'Profile',
  properties: {
    name: { type: 'string', title: 'Name' },
    age: { type: 'integer', title: 'Age', minimum: 18 },
  },
  required: ['name'],
}

const adapter = await createJsonSchemaAdapter(schema)
const registry = createDefaultRegistry()

function App() {
  const form = useForm(adapter, {
    initialData: {
      name: '',
      age: 18,
    },
  })

  return (
    <FormProvider value={form.runtime}>
      <FormRoot registry={registry} />
      <pre>{JSON.stringify(form.data, null, 2)}</pre>
    </FormProvider>
  )
}

createRoot(document.getElementById('root')!).render(<App />)

React observes the Texaryn runtime. It does not evaluate JSON Schema or own the canonical form state.

Main APIs

useForm(port, options?)

Creates a Texaryn runtime for a React component and subscribes to its top-level stores.

const form = useForm(port, {
  initialData,
  hints,
  onSubmit,
})

It returns:

interface UseFormReturn {
  runtime: FormRuntime
  document: UIDocument
  data: unknown
  submission: SubmissionState
  dispatch(command: Command): void
  getNodeState(nodeId: NodeId): NodeState | undefined
}

The runtime is destroyed automatically when the hook unmounts.

FormProvider

Provides a FormRuntime to renderer components and hooks, and owns the DOM id namespace for that rendering surface:

<FormProvider value={form.runtime}>
  <ErrorSummary />
  <FormRoot registry={registry} />
</FormProvider>

messages takes a whole FormMessages set from @texaryn/core and replaces every word the built-in widgets invent. See the localizing guide on the docs site. Custom widgets read it with useFormMessages().

useFormContext() returns the current runtime and useFormIdPrefix() returns the namespace. Rendering through FormContext.Provider instead throws: the context alone carries no namespace, which is what let two forms on one page share ids.

Generated ids are opaque relationship identifiers rather than styling hooks. Their shape is not part of the public contract; use classes or data attributes for CSS. Uniqueness is per React application, so two independent roots on one page need distinct identifierPrefix options.

FormRoot

Renders the root node of the current UIDocument using a renderer registry:

<FormRoot registry={registry} />

NodeRenderer

Renders an individual semantic UI node and resolves the matching widget through the registry.

Field hooks

The package exports:

  • useField
  • useFieldArray
  • useStore

These hooks bridge React subscriptions to the runtime's framework-neutral stores and commands.

Prop getters

For custom widgets and design systems, Texaryn exports accessible prop helpers:

  • getInputProps
  • getLabelProps
  • getErrorProps
  • getDescriptionProps

They translate semantic field state into DOM-facing props without moving form behavior into React components. getInputProps also carries the field's placeholder hint, so any widget that spreads its result onto an input shows it.

Error display

useField returns showErrors, which is true once a field is invalid and either touched or the form has been submitted at least once. Widgets use it to decide whether to render inline errors:

import { FieldErrors } from '@texaryn/react'

<FieldErrors node={node} errors={errors} showErrors={showErrors} />

ErrorSummary renders a named group, headed by an h2, listing every visible error with a link to its field, and takes focus once a failed submit settles, once per attempt. Pass focus={false} on all but one summary when one runtime is rendered twice. The heading and the text after each link come from errorSummaryHeading and errorSummaryDetail in FormMessages.

import { ErrorSummary } from '@texaryn/react'

<FormProvider value={form.runtime}>
  <ErrorSummary />
  <FormRoot registry={registry} />
</FormProvider>

All default widgets already render FieldErrors internally, so most applications only need to add ErrorSummary.

Submission

useForm exposes the runtime's submission state and a dispatch function. Start a submission by dispatching Submit:

<button
  type="button"
  disabled={
    form.submission.status === 'validating' ||
    form.submission.status === 'submitting'
  }
  onClick={() => form.dispatch({ type: 'Submit' })}
>
  Submit
</button>

form.submission.status moves through idle, validating, submitting, and submitted. An invalid validation result returns the status to idle and shows the errors on every invalid field, touched or not, without setting form.submission.error. form.submission.attempts counts the accepted Submit commands since the last Reset. The error is set only when the validator throws or rejects, or when onSubmit throws or rejects; the status returns to idle in those cases too:

{form.submission.error != null && (
  <p>{String(form.submission.error)}</p>
)}

React does not own the submission lifecycle. It only observes the submission store that @texaryn/core computes; see the @texaryn/core README for the full snapshot semantics.

Custom widgets

useFieldBinding(node) composes everything a field widget needs: the value and setValue, the label, description and placeholder, display-gated errors with a first error and an invalid flag, the ARIA prop getters, and two event-shaped adapters ready to spread onto a native control, domInputProps for value controls and domCheckboxProps for checkboxes. They own the display value and the coercion for text, number, checkbox and select controls, so a widget is markup around one hook:

import { useFieldBinding } from '@texaryn/react'
import type { FieldNode, UINode } from '@texaryn/core'

function BootstrapTextInput({ node }: { node: UINode }) {
  const field = useFieldBinding(node as FieldNode)
  return (
    <div className="mb-3">
      <label {...field.labelProps} className="form-label">{field.label}</label>
      <input {...field.domInputProps} type="text" className={field.invalid ? 'form-control is-invalid' : 'form-control'} />
      {field.description ? <div {...field.descriptionProps} className="form-text">{field.description}</div> : null}
    </div>
  )
}

setValue(value) is the fundamental operation; the onChange on the DOM surfaces is a convenience that coerces the DOM event and calls it. A component that deals in values directly uses field.value and field.setValue instead. Checkboxes spread domCheckboxProps (checked); text, number, textarea and select controls spread domInputProps (value). The lower-level getInputProps, getLabelProps, getErrorProps and getDescriptionProps remain available and unchanged.

A renderer that shows either the error or the description, not both, owns aria-describedby for what it renders; the binding's props reference both elements because the default widgets render both.

Default widgets

createDefaultRegistry() registers the built-in React widgets:

  • TextInput
  • NumberInput
  • Checkbox
  • Select
  • Textarea
  • ObjectLayout
  • ArrayControl
import { createDefaultRegistry } from '@texaryn/react'

const registry = createDefaultRegistry()

The default registry is intentionally small. Applications can supply their own registry and widget components for a product or design system.

ArrayControl renders Add and Remove when the array hints allow them. With canReorder: true, each row also gets Up and Down controls when a move in that direction is available. Moving a row preserves its mounted fields.

Custom rendering

The renderer boundary is registry-based. React components are selected from semantic node information rather than from JSON Schema keywords directly.

This keeps the dependency direction clear:

@texaryn/schema-json
        |
        v
   @texaryn/core
        |
        v
   @texaryn/react

The schema adapter interprets data semantics. The core compiles and runs the form. React renders the result.

Public exports

Hooks

  • useStore
  • useForm
  • UseFormReturn
  • useField
  • UseFieldReturn
  • useFieldArray
  • UseFieldArrayReturn
  • useFieldBinding
  • useObjectGroup
  • useArrayActions
  • ArrayActions
  • ObjectGroup
  • FieldBinding
  • DomValueInputProps
  • DomCheckedInputProps
  • DomInputBaseProps
  • useFormContext
  • useFormMessages

Context and rendering

  • FormContext
  • FormProvider
  • FormProviderProps
  • useFormIdPrefix
  • FormRoot
  • FormRootProps
  • NodeRenderer
  • NodeRendererProps
  • useRendererContext
  • WidgetComponent
  • RendererContextValue

Error display

  • FieldErrors
  • FieldLabelContent
  • FieldLabelContentProps
  • FieldErrorsProps
  • ErrorSummary

Prop getters

  • getInputProps
  • getLabelProps
  • getErrorProps
  • getDescriptionProps
  • InputProps
  • LabelProps
  • ErrorProps
  • DescriptionProps
  • FieldState

Widgets

  • TextInput
  • NumberInput
  • Checkbox
  • Select
  • Textarea
  • ObjectLayout
  • ArrayControl
  • createDefaultRegistry

Related packages

See the repository README for the complete architecture.

License

Apache-2.0