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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@fab4m/builder

v1.0.0-alpha4

Published

A form builder UI

Readme

Form builder

The @fab4m/builder package provides the basic building blocks to create a visual form builder UI capable of constructing fab4m forms.

Installing

Install @fab4m/builder package.

npm install --save @fab4m/builder

The form builder is built using tailwind css, so you will need to have that installed as well. Refer to their documentation page for information on how to do that.

Set up the form you want to edit

The form builder works with any serialized fab4m form, for example:

import { crateForm, serialize, textField } from "@fab4m/fab4m";
const formToEdit = serialize(
  createForm({
    component: textField({ label: "First component" }),
    second: textField({ label: "Second component" }),
  }),
);

Add the form builder context

Place any components that should edit the form within the <Form@fab4m/builderContext> component. This will allow you to use the form builder hooks to manipulate the form.

The form builder context takes in plugins for the different components, widgets and validators you want to be able to work with inside of the form. Plugins for all fab4m core features are provided in the @fab4m/builder package.

an export containing all plugins is provided for convenience.

import { allPlugins, From@fab4m/builderContext } form "@fab4m/builder"
<Form@fab4m/builderContext form={formToEdit} plugins={allPlugins}>
/* You can use any part of the form builder in here. */
</Form@fab4m/builderContext>

The FormComponents component

Start by adding the <FormComponents> component. This will give you a drag and drop interface where you can drag your components around.

import React, { useState } from "react";
import {
  FormComponents,
  Form@fab4m/builderProvider,
  allPlugins,
} from "@fab4m/builder";
import { createForm, serialize, textField } from "@fab4m/fab4m";

// The form builder works on the serialized version of the form.
const form = serialize(
  createForm({
    component: textField({ label: "First component" }),
    second: textField({ label: "Second component" }),
  }),
);

export default function FormComponentsExample() {
  const [draft, changeDraft] = useState(form);
  return (
    <Form@fab4m/builderProvider
      form={draft}
      formChanged={changeDraft}
      plugins={allPlugins}
    >
      <FormComponents />
    </Form@fab4m/builderProvider>
  );
}

Adding actions to each component

In the first step we're just able to drag the components around. The next step is to be able to perform actions on them.

We do this by adding actions to each component:

import { FormComponents, Form@fab4m/builderProvider } from "@fab4m/builder";
import { createForm, serialize, textField } from "@fab4m/fab4m";
import React, { useState } from "react";

const form = serialize(
  createForm({
    component: textField({ label: "First component" }),
    second: textField({ label: "Second component" }),
  }),
);

export default function FormActionsExample() {
  const [draft, changeDraft] = useState(form);
  return (
    <Form@fab4m/builderProvider form={draft} formChanged={changeDraft}>
      <FormComponents
        actions={({ component, removeComponent, updateComponent }) => (
          <>
            <button type="button" onClick={removeComponent}>
              Remove
            </button>
            <button
              type="button"
              onClick={() =>
                updateComponent({ ...component, label: "Changed component" })
              }
            >
              Change component
            </button>
          </>
        )}
      />
    </Form@fab4m/builderProvider>
  );
}

Adding new components to the form

The NewComponent component can be used to easily provide a gallery of all availbale components that you can add, and offers the user an option to add them:

import React, { useState } from "react";
import {
  FormComponents,
  Form@fab4m/builderProvider,
  allPlugins,
  NewComponent,
} from "@fab4m/builder";
import { createForm, serialize } from "@fab4m/fab4m";

const form = serialize(createForm({}));

export default function NewComponentsExample() {
  const [draft, changeDraft] = useState(form);
  return (
    <Form@fab4m/builderProvider
      form={draft}
      formChanged={changeDraft}
      plugins={allPlugins}
    >
      <FormComponents />
      <h2>Add new component</h2>
      <NewComponent
        attributes={{
          name: `component_${draft.components.length}`,
          label: `Component ${draft.components.length + 1}`,
        }}
      />
    </Form@fab4m/builderProvider>
  );
}

Editing components

You can use the EditFormComponent to be able to edit any component in the form:

import React, { useState } from "react";
import {
  FormComponents,
  Form@fab4m/builderProvider,
  allPlugins,
  EditFormComponent,
} from "@fab4m/builder";
import { createForm, serialize, textField } from "@fab4m/fab4m";

const form = serialize(
  createForm({
    item: textField({ label: "Item" }),
  }),
);

export default function NewComponentsExample() {
  const [draft, changeDraft] = useState(form);
  const [editComponent, changeEditComponent] = useState<null | string>(null);
  return (
    <Form@fab4m/builderProvider
      form={draft}
      formChanged={changeDraft}
      plugins={allPlugins}
    >
      <FormComponents
        actions={(props) => (
          <button onClick={() => changeEditComponent(props.formKey)}>
            Edit
          </button>
        )}
      />
      {editComponent ? (
        <dialog open={true} className="backdrop:bg-gray-50">
          <EditFormComponent
            componentKey={editComponent}
            componentSaved={() => changeEditComponent(null)}
          />
        </dialog>
      ) : null}
    </Form@fab4m/builderProvider>
  );
}

Full example

The example below combines all the bits below to make a complete form builder.

import React, { useState } from "react";
import {
  FormComponents,
  Form@fab4m/builderProvider,
  allPlugins,
  NewComponent,
  EditFormComponent,
} from "@fab4m/builder";
import { createForm, serialize } from "@fab4m/fab4m";

const form = serialize(createForm());
export default function FullExample() {
  const [draft, changeDraft] = useState(form);
  const [currentKey, changeCurrentKey] = useState<null | string>(null);
  return (
    <Form@fab4m/builderProvider
      form={draft}
      formChanged={changeDraft}
      plugins={allPlugins}
    >
      <FormComponents
        actions={(props) => (
          <button type="button" onClick={() => changeCurrentKey(props.formKey)}>
            Edit
          </button>
        )}
      />
      {currentKey ? (
        <div className="border bg-slate-100 p-4">
          <EditFormComponent
            componentKey={currentKey}
            componentSaved={() => changeCurrentKey(null)}
          />
        </div>
      ) : (
        <div className="border bg-slate-100 p-4">
          <NewComponent
            attributes={{
              name: `component_${draft.components.length}`,
              label: `Component ${draft.components.length + 1}`,
            }}
          />
        </div>
      )}
    </Form@fab4m/builderProvider>
  );
}

License

All the code is licensed under the GPL v3 License.