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

@formflowjs/core

v1.0.3

Published

Framework-agnostic core for FormFlow: types, schema-driven form engine, validation/conditional logic (parity with the Strapi plugin), and the content-API client.

Readme

@formflowjs/core

Framework-agnostic engine for FormFlow — the headless form renderer for the @formflowjs/strapi-plugin-formflow Strapi plugin.

This package owns everything except the rendering: schema typing, a content-API client, client-side validation with parity to the server, conditional visibility, multi-step flow, file uploads, captcha/honeypot plumbing, and a reactive form store. It imports no framework and ships no CSS. It is SSR/RSC-safe — no window/document access at module load.

Use it directly, or reach for the thin adapters:

Install

npm install @formflowjs/core
# or: pnpm add @formflowjs/core / yarn add @formflowjs/core

createFormFlowClient — talk to the content-API

import { createFormFlowClient } from '@formflowjs/core';

const client = createFormFlowClient({
  baseUrl: 'https://cms.example.com', // origin; '' for same-origin
  // apiPrefix: '/api/formflow',      // default
  // headers: { 'x-cdn-token': '…' }, // optional extra headers
  // fetch: customFetch,              // injectable for SSR / tests
});

// Fetch a public schema by slug (optionally per-locale).
const schema = await client.getForm('contact', { locale: 'en' });

// Submit (flat body; multipart when a file field holds a File).
const result = await client.submit('contact', {
  schema,
  values: { email: '[email protected]', message: 'Hi' },
});
// result: { success: true, message, redirectUrl }

Every method rejects with a typed FormFlowError on failure — branch on error.code ('validation' | 'rate_limited' | 'network' | …) instead of HTTP statuses. error.fieldErrors carries server validation messages keyed by field name; error.retryAfter holds the rate-limit cooldown in seconds.

import { isFormFlowError } from '@formflowjs/core';

try {
  await client.submit('contact', { schema, values });
} catch (err) {
  if (isFormFlowError(err) && err.code === 'validation') {
    console.log(err.fieldErrors);
  }
}

createFormStore — reactive form state

A single immutable-state observer store. Every mutation replaces state and notifies subscribers, so it drives both React's useSyncExternalStore and Vue's shallowRef.

import { createFormStore } from '@formflowjs/core';

const store = createFormStore(schema, {
  baseUrl: 'https://cms.example.com', // or pass `client`
  validateOn: 'blur',                 // 'change' | 'blur' | 'submit'
  onSubmitSuccess: (result) => { /* navigate to result.redirectUrl, etc. */ },
});

store.subscribe(() => render(store.getState()));

store.setFieldValue('email', '[email protected]'); // recomputes visibility + clears hidden errors
store.setFieldTouched('email');                  // validate-on-blur

// Multi-step:
await store.nextStep();   // validates the current step (client, or server)
store.prevStep();

// Captcha (collect the token from your widget):
store.setCaptchaToken('recaptcha', token);

// Submit (validates first — never hits the network when invalid):
const { ok, result, error } = await store.submit();

State snapshot (store.getState()) exposes values, errors, touched, dirty, visibleFieldNames, currentStep/stepCount, status, isSubmitting, submitError, result, uploadProgress, and resumeToken.

Analytics and server-only behavior

Headless by design — no markup, no styles, and no widget rendering. A store records one best-effort start event per session: normally when interaction begins, or through first-step validation when server-side step validation is enabled (with a submit fallback). Form-schema fetches and successful submissions record views and completions server-side. Consumers using the client without a store can call client.trackStart?.(slug) once when interaction begins. Analytics failures never block form use.

Webhooks, email, exports, and autoresponders are server-only concerns.

License

MIT