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

jsonapi-svelte-form

v0.0.20

Published

Tooling for building forms in Svelte for JSON:API backends.

Readme

JSON:API Svelte Form

Tooling for building forms in Svelte for JSON:API backends.

If you use Svelte to build business webapps, and those webapps interact with JSON:API, than you've probably thought about building some tooling to help make it easier to build good forms, without so much boilerplate.

How to Use

To import the Svelte components (documented below):

import {
	Field,
	FieldSetter,
	Form,
	FormCreate,
	FormRemove,
	MakeDiff,
	Relationship
} from 'jsonapi-svelte-form'

To import the mapper functions:

import { saving, error, saved } from 'jsonapi-svelte-form/mapper'

The Big Idea

Build a form with a couple Svelte component wrappers:

<script>
	import { Form, Field } from 'jsonapi-svelte-form'
	export let form // a specially formed object
</script>
<Form bind:form let:remove let:create>
	Inside your form, bind a property to an input element:
	<Field bind:form let:set let:value id="id001" keypath={[ 'attributes', 'color' ]}>
		<input type="text" {value} on:input={event => set(event.target.value)} />
	</Field>
</Form>

Now, you can bind values using JSON Pointer paths.

There's a demo here, or as a Svelte REPL, to see how to use the tooling.

Data Structure

When you get a response from a JSON:API server, you map it to a JsonApiForm object, typically using the load function:

import { load } from 'jsonapi-svelte-form/mapper'
const fetchVehicle = () => fetch('/api/v1/vehicles/id001')
	.then(response => response.json())
	.then(load)

That structure of that object looks like this:

const JsonApiForm = {
	// This is the data you'll mutate with your form:
	data: {
		id001: {
			id: 'id001',
			type: 'car',
			attributes: { /* ... */ }
		}
	},
	// A copy is kept around that isn't allowed to change...
	original: {
		id001: {
			id: 'id001',
			type: 'car',
			attributes: { /* ... */ }
		}
	},
	// ...that way the changes between the two can be calculated:
	changes: {
		id001: [
			{
				op: 'add',
				// (Note: the changes are JSON Patch objects, but
				// the path is the array of accessors, instead of
				// the escaped string.)
				path: [ 'attributes', 'color' ],
				value: 'red'
			}
		]
	},
	// If there are errors on a response, they can be mapped to this error
	// object, which is a map to each resource:
	errors: {
		id001: {
			attributes: {
				color: [
					// Each object is a valid JSON:API error object:
					// https://jsonapi.org/format/#error-objects
					{
						code: 'TheServerErrorCode',
						title: 'Human-readable summary.',
						// etc.
					}
				]
			}
		}
	}
}

Field Component

When the set function of the Field component is called, e.g.:

<Field bind:form let:set let:value id="id001" keypath={[ 'attributes', 'color' ]}>
	<input type="text" {value} on:input={event => set(event.target.value)} />
</Field>

the component updates the appropriate form.data property, and then updates the form.changes list by doing a diff against the form.original property.

Note: your component is responsible for handling the difference between undefined and empty-string.

In the example above, when the input element is made to be empty, the default event.target.value is the empty string, so the form.data property would be set to the empty string. This matters when calculating the changes list for the form object: if the property was originally undefined and a change event is emitted where value is the empty string, the form.changes list will not be empty.

One way to handle that difference is simply:

<input ... on:input={event => set(event.target.value || undefined)} />

This may be wanted or unwanted behaviour, so it is left up to your implementation to handle the difference.

Field Component API

Required properties to set on the Field component:

  • form: JsonApiForm - The form object needs to be bound for the reactivity to work.
  • id: String - The resource identifier to bind to.
  • keypath: String | Array<String|Number> - Either the JSON Pointer string, e.g. "/path/to/thing" or the list, e.g. [ "path", "to", "thing" ]. (Note: using the string incurs a performance penalty, since it will be converted to a list in the Field component.)

Optional properties:

  • debounceMillis: Integer (default: 15) - On every change, the diff between original and updated is calculated. This can get very expensive, and since it blocks the UI, it can cause the form to feel very jerky if many changes are made quickly. To counteract this, there is a debounce on the diff calculation, and you can modify the debounce delay with this property.

Emitted events:

change - Emitted after an object has been updated and the diff has been calculated. It emits an object with these properties.

  • id: String - The resource identifier.
  • keypath: Array<String> - The JSON Pointer accessor tokens.
  • value: any - The set value.

Slot properties:

  • value: any - The value located at the resources keypath, or undefined.
  • errors: Array<JsonApiError> - The list of errors, or an empty list.
  • set: Function - Call this with the updated value, when it changes.
  • disabled: Boolean - A convenient property which is true if the form is in the saving or loading state.

Form Component

The Form component is responsible for handling creating and removing resources, so at the root of your form you'd have something like:

<Form bind:form let:remove let:create on:create on:remove>
	<button on:click={() => create(resource)}Create</button>
</Form>

New resources are placed on the form.data object, with an id generated using a configurable prefix (by default GID) and an incrementing counter, e.g. GID1 will be the id of the first generated resource.

The create function requires new resources to have their relationship defined, so e.g. on a car form you might make a wheel resource, but that relationship would need to be defined.

Form Component Api

Required properties to set on the Form component:

  • form: JsonApiForm - The form object needs to be bound for the reactivity to work.

Optional properties:

  • prefix: String (default: GID) - The prefix used on the identifiers of created resources.
  • suffix: String (default: blank string) - The suffix used on the identifiers of created resources.

Slot properties:

  • create: Function - Used to create a new resource with a generated identifier. Call with an object containing these properties:
    • relId: String - The identifier of the resource to add this to, as a relationship.
    • relName: String - The relationship accessor name of the relationship.
    • isArray: Boolean (optional) - Set to true if the relationship is an array style.
    • type: String - The type of the resource to create.
    • resource: Object - The initial state of the created resource, with id and type set, e.g. resource = { attributes, meta }.
  • remove: Function - Used to remove a resource. Call with an object containing these properties:
    • id: String - The identifier of the resource to remove.
    • type: String - The type of the resource to remove.
  • disabled: Boolean - A convenient property which is true if the form is in the saving or loading state.

License

Published and released under the Very Open License.