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

@canonical/react-ds-global-form

v0.30.0

Published

Form components for the Pragma design system. This package provides a field system built on react-hook-form with two core patterns: a field switch for rendering different input types and middleware composition for extending field behavior.

Readme

@canonical/react-ds-global-form

Form components for the Pragma design system. This package provides a field system built on react-hook-form with two core patterns: a field switch for rendering different input types and middleware composition for extending field behavior.

Prerequisites

  • React 19 or higher

Installation

bun add @canonical/react-ds-global-form @canonical/styles

Import the global styles and the form component styles in your application's root stylesheet:

@import url("@canonical/styles");
@import url("@canonical/react-ds-global-form/dist/esm/index.css");

The global styles provide the CSS reset, typography baseline, and design tokens (colour, spacing, surfaces, states) that all form components depend on. The form stylesheet provides input chrome, field layout, and component-specific styles.

The package builds on top of @canonical/react-ds-global.

Dependencies

The form system builds on two key libraries:

  • react-hook-form - Form state management, validation, and field registration. The Field component uses useFormContext internally, so forms must be wrapped in a FormProvider.
  • downshift - Powers the combobox field with accessible autocomplete behavior.

Usage

Wrap your form in a Form component and use Field for inputs:

import { Form, Field } from "@canonical/react-ds-global-form";

function ContactForm() {
  const onSubmit = (data) => console.log(data);

  return (
    <Form onSubmit={onSubmit}>
      <Field
        name="email"
        inputType="email"
        label="Email address"
        description="We'll never share your email."
      />
      <Field
        name="message"
        inputType="textarea"
        label="Message"
      />
      <button type="submit">Send</button>
    </Form>
  );
}

Form modes

Form wraps its children in a react-hook-form FormProvider and works in two modes:

  • Internal mode (above) — pass onSubmit, and optionally defaultValues and a validation mode; Form creates the useForm instance for you. Best for a self-contained form.

  • External mode — create the useForm instance yourself and pass it as methods. Because you own it, you can read formState (e.g. isSubmitting) and call its methods (reset, setValue, watch, …). Use this for async submits, shared state, or multi-step forms.

    import { useForm } from "react-hook-form";
    
    const methods = useForm({ mode: "onBlur", defaultValues: { email: "" } });
    const { reset, formState: { isSubmitting } } = methods;
    
    <Form methods={methods} onSubmit={async (data) => { await save(data); reset(); }}>
      <Field name="email" inputType="email" label="Email" />
      <button type="submit" disabled={isSubmitting}>Send</button>
    </Form>;

    When you pass methods, Form's own defaultValues/mode props are ignored — configure those on your useForm call.

This library is a thin layer over react-hook-form (^7.71): validation rules (registerProps), formState, submission, field arrays, and schema resolvers are all RHF's API. See the Getting Started guide in Storybook for a full walkthrough with runnable examples, and the react-hook-form docs for the complete surface.

Field Switch Pattern

The Field component uses inputType to select the appropriate input component:

| inputType | Component | Description | |-----------|-----------|-------------| | text, email, password, number, tel, url | Text | Standard text inputs | | textarea | Textarea | Multi-line text | | checkbox | Checkbox | Boolean toggle | | range | Range | Slider input | | select | Select | Dropdown selection | | simple-choices | SimpleChoices | Radio buttons or checkboxes | | combobox | Combobox | Searchable dropdown | | hidden | Hidden | Hidden input | | custom | Your component | Pass via CustomComponent prop |

All fields are wrapped with withWrapper, which provides form registration, labels, descriptions, error display, and middleware support.

Custom Fields

For field types not covered by the built-ins, use inputType="custom":

import { Field } from "@canonical/react-ds-global-form";
import { MyColorPicker } from "./MyColorPicker";

<Field
  name="brandColor"
  inputType="custom"
  CustomComponent={MyColorPicker}
  label="Brand Color"
/>

Custom components must be wrapped with withWrapper. Custom components must satisfy the InputProps type and integrate with react-hook-form via useFormContext.

Middleware Pattern

Middleware are higher-order components that wrap fields to add functionality. They compose via the middleware prop:

<Field
  name="country"
  inputType="select"
  label="Country"
  middleware={[addRESTOptions("/api/countries")]}
/>

The middleware signature is (Component) => Component. Multiple middleware compose in array order, with the first middleware as the outermost wrapper.

Built-in Middleware

addRESTOptions - Fetches options from an API endpoint:

import { addRESTOptions } from "@canonical/react-ds-global-form";

<Field
  name="category"
  inputType="select"
  middleware={[
    addRESTOptions("/api/categories", {
      transformData: (data) => data.categories,
    }),
  ]}
/>

addRESTValidation - Validates field values against an API:

import { addRESTValidation } from "@canonical/react-ds-global-form";

<Field
  name="username"
  inputType="text"
  middleware={[
    addRESTValidation("/api/validate-username", {
      debounceWait: 300,
      minLength: 3,
    }),
  ]}
/>

Custom middleware follows the same factory pattern: an outer function accepts configuration and returns a HOC.

Conditional Display

Fields can conditionally render based on other field values:

<Field
  name="company"
  inputType="text"
  label="Company"
  condition={[
    ["accountType"],
    ([type]) => type === "business",
  ]}
/>

The field only renders when the condition function returns true.

Storybook

cd packages/react/ds-global-form
bun run storybook

The Storybook configuration includes MSW integration for mocking backend responses.

Component Specifications

Form component specifications are defined in the Design System Ontology.