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

litefyr-dynamic-form-elements

v0.1.1

Published

Re-usable dynamic form elements for Svelte.

Readme

DynamicForm Presentation Components

These components provide the reusable UI layer of DynamicForm. They render markup and encapsulate UI behavior, while form state, server communication, and field validation are supplied by the surrounding form integration.

Import

All components are exported through index.ts:

import { Button, Pagination } from "./Presentation";

A component can also be imported directly:

import Button from "./Presentation/Button.svelte";

The components use Svelte 5 runes and are configured through $props().

Component Overview

| Component | Purpose | | ------------------ | ------------------------------------------------------- | | Button | Consistently styled button with snippet content | | Dropzone | File selection, file previews, and file errors | | Fieldset | Semantic field grouping with an optional legend | | FormElement | Renderer for DynamicForm field types | | GroupHeadline | Headline for field groups | | LabelWrapper | Label and layout wrapper for fields | | LoadingIndicator | Delayed spinner and loading indicator | | Pagination | Previous/next/submit navigation for multi-page forms | | RichTextEditor | Rich-text editor with toolbar and editor configuration | | SendButton | Submit button with loading and success states | | Textarea | Styled textarea with dynamic minimum and maximum height | | Validation | Validation message and help-text output |

Button

Button renders a <button> with the project classes btn, btn--inline, and either btn--theme or btn--link.

Props

| Prop | Type | Default | Description | | ---------- | --------------------------------- | ---------- | ------------------------------ | | theme | "nested" \| "link" | "nested" | Selects the button variant | | type | "button" \| "submit" \| "reset" | "button" | Native button type | | before | unknown | "" | Content before the button text | | after | unknown | "" | Content after the button text | | children | Snippet | - | Button content |

Additional props such as disabled, name, value, and event handlers are forwarded to the native button.

<Button theme="link" onclick={cancel}>Cancel</Button>
<Button type="submit" disabled={sending}>Submit</Button>

Dropzone

Dropzone is the presentation layer for file uploads. It manages accepted files, drag state, previews, file reading, and dropzone error messages. The surrounding form integration receives changes through callbacks.

Main Props

| Prop | Type | Description | | ------------------- | --------------------------------------- | ------------------------------------------------------------------------------- | | name | string | Field name | | id | string | ID of the associated file input | | field | Record<string, any> | Field definition, for example multiple, accept, maxSize, or previewSize | | formSettings | { labels: Record<string, string> } | Labels for the dropzone and error messages | | onValueChange | (value: File \| File[] \| "") => void | Called when files are selected or removed | | onValidationError | (message: string \| false) => void | Receives the current dropzone error | | onValidate | () => void | Called when the file dialog is cancelled |

<Dropzone
  name="attachments"
  id="attachments"
  field={{ multiple: true, accept: ["image/png"], previewSize: 160 }}
  {formSettings}
  onValueChange={(value) => (formValues.attachments = value)}
  onValidationError={(message) => (validationErrors.attachments = message)}
  onValidate={validateAttachments}
/>

Do not duplicate file and preview processing in the surrounding form integration.

Fieldset

Fieldset renders a native <fieldset> for grouping related form controls. It can render an optional legend and forwards additional attributes such as disabled, name, or aria-* to the native element. Child content is provided through a Svelte snippet.

| Prop | Type | Default | Description | | ------------- | --------- | --------------------- | ------------------------------------ | | legend | string | - | Optional fieldset legend | | legendClass | string | "block text-fl-2xl" | Classes applied to the legend | | class | string | - | Additional fieldset classes | | children | Snippet | - | Content rendered inside the fieldset |

<Fieldset legend="Contact details">
  <input name="email" type="email" />
</Fieldset>

FormElement

FormElement renders a DynamicForm field based on its type, such as checkbox, checkboxGroup, radioGroup, select, textarea, rte, file, or a standard input.

The following example shows the complete contract between the presentation renderer and a stateful form integration:

<FormElement
  name="email"
  type="email"
  label="Email"
  required={true}
  field={{ placeholder: "[email protected]" }}
  formValues={{ email: value }}
  validationMessages={[validationError]}
  validate={handleInput}
  validateWithOutput={handleBlur}
/>

In a complete form, the integration supplies the current field value and the callbacks that connect validation and state updates.

FormElement Props

| Prop | Type | Description | | -------------------- | ------------------------ | -------------------------------------------------- | | name | string | Field name | | type | string | Field type | | field | Record<string, any> | Native field attributes and field-specific options | | label | string | Field label | | required | boolean | Required-field state | | formValues | Record<string, any> | Bindable form values | | validationMessages | Array<string \| false> | Messages passed to Validation | | validate | (event: Event) => void | Inline validation callback | | validateWithOutput | (event: Event) => void | Validation callback that outputs the result |

GroupHeadline

Renders a headline for checkbox and radio groups.

<GroupHeadline label="Interests" required={true} />

| Prop | Type | Description | | ---------- | --------- | ------------------------------- | | label | string | Headline text | | required | boolean | Shows the required-field marker |

LabelWrapper

Arranges a label, field content, and optional validation output. Checkbox and radio fields automatically use a different content order.

| Prop | Type | Description | | ------------ | ------------------------------- | ------------------------------------------------- | | label | string | Label text | | required | boolean | Required-field marker | | type | "checkbox" \| "radio" \| null | Affects layout and label position | | group | boolean | Removes the normal top spacing for grouped fields | | validation | Snippet | Optional validation output | | class | string | Additional CSS class | | cols | number | Number of grid columns | | id | string | ID associated with the field | | children | Snippet | Field content |

<LabelWrapper label="Email" required={true} id="email">
  <input id="email" name="email" type="email" />
</LabelWrapper>

LoadingIndicator

Shows a spinner after a delay, followed by animated dots. Nothing is rendered when showLoading={false}.

| Prop | Type | Default | Description | | -------------- | --------- | ------- | ------------------------------------------------- | | showLoading | boolean | true | Visibility | | delayTime | number | 500 | Delay before showing the spinner, in milliseconds | | timeoutTime | number | 5000 | Time before switching to the dots animation | | size | number | 24 | SVG size | | dynamicWidth | boolean | false | Animates the width between spinner and dots | | title | string | null | HTML title | | class | string | null | Additional CSS class |

<LoadingIndicator showLoading={loading} title="Loading" dynamicWidth={true} />

Pagination

Pagination encapsulates multi-page form navigation. On the last page it automatically renders SendButton instead of the next button.

| Prop | Type | Description | | ----------------- | ----------------------------------- | ---------------------------------------- | | showPrevious | boolean | Shows the previous button | | hasNextPage | boolean | Selects next or submit state | | previousLabel | string | Previous-button label | | nextLabel | string | Next-button label | | submitLabel | string | Submit-button label | | submittingLabel | string | Loading-indicator label | | sending | boolean | Submit is in progress | | submitted | boolean | Form was submitted successfully | | allowResend | boolean | Allows submitting again | | successMessage | string \| null | Success message | | onPrevious | () => void | Callback for moving to the previous page | | validateForm | () => boolean \| Promise<boolean> | Validation before moving or submitting |

RichTextEditor

RichTextEditor contains the toolbar, Typewriter editor, heading levels 2 through 6, Markdown input behavior, and HTML synchronization. It accepts an external value and reports changes through onValueChange.

| Prop | Type | Description | | --------------- | ------------------------------------------------------------- | ---------------------------------- | | value | formValue | Bindable HTML value | | name | string | Field name | | placeholder | string | Editor placeholder | | id | string | Input ID | | required | boolean | Required-field state | | formLabels | Record<string, string> | Default ARIA labels from the form | | ariaLabels | Partial<RteAriaLabels> | Overrides individual ARIA labels | | onValueChange | (value: string) => void | Receives the normalized HTML value | | validateFunc | (element?: HTMLInputElement) => boolean \| Promise<boolean> | Validation callback |

<RichTextEditor name="description" bind:value={description} formLabels={formSettings.labels} onValueChange={(value) => (formValues.description = value)} validateFunc={validateDescription} />

SendButton

SendButton is the form submit control. It renders the submit button, loading indicator, and success message.

| Prop | Type | Description | | ----------------- | ----------------------------------- | -------------------------------------- | | label | string | Current button label | | submittingLabel | string | Loading-indicator label | | sending | boolean | Disables the button and shows loading | | submitted | boolean | Prevents another submit unless allowed | | allowResend | boolean | Allows submitting again | | successMessage | string \| null | Success message | | validateForm | () => boolean \| Promise<boolean> | Submit validation callback |

Textarea

Textarea is a thin wrapper around <textarea>. All unknown props are forwarded to the native element.

| Prop | Type | Default | Description | | ----------- | -------- | ------- | ------------------------------- | | value | string | "" | Bindable value | | minHeight | number | 2 | Minimum line height used by CSS | | maxHeight | number | 6 | Maximum line height used by CSS |

<Textarea bind:value={comment} rows={4} name="comment" />

Validation

Renders validation messages and optional help text. Messages with the value false are ignored. Messages are rendered as HTML, so they must come from a trusted source.

| Prop | Type | Default | Description | | ---------- | ------------------------ | ------- | ---------------------------- | | messages | Array<string \| false> | [] | Validation messages | | help | string | "" | Help text below the messages |

<Validation messages={[clientError, serverError]} help="Maximum 500 characters" />

Architecture Rules

  • Presentation components contain markup, layout, and UI-specific behavior.
  • The stateful form integration owns form state, server communication, and validation functions.
  • Form state crosses the boundary through props and callbacks.
  • New Presentation components must not import internal files outside this directory.
  • External UI libraries such as svelte-dropzone-runes or typewriter-editor are allowed when the component renders their UI directly.
  • New public components must also be exported from index.ts.