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

@thenewdynamic/astro-forms

v0.1.0

Published

Astro integration for building forms from structured data

Readme

HUGE can build forms from a few config lines

TL;DR:

Setup

// astro.config.mjs
import tndForms from "./src/packages/forms"
export default defineConfig({
  site: 'https://something.com',
  intergrations: [
    tndForms({
      submitCopy: 'Get in touch',
    })
  ]
})

Usage

---
import type { FormType } from '@forms/types'
import Form from "@forms/components/Form.astro";
const contactForm: FormType = {
  netlify: {
    id: 'contact'
  },
  captcha: true,
  redirect: "/thank-you/",
  fields: [
    {
      name: 'name',
      required: true,
      label: 'Name',
    },
    {
      // Group Field won't display a "<legend>" unless the 'label' key is explicitly set.
      name: 'contacts',
      type: 'group',
      columns: 2,
      fields: [
        {
          name: 'email',
          label: 'Email Address',
          type: 'email',
          required: true,
        },
        {
          name: 'phone',
          label: 'Phone Number',
          type: 'phone'
        },
      ]
    },
    {
      name: "company_size",
      label: "Company Size",
      type: "select",
      options: ["1 - 5", "6-20", "more"]
    },
    {
      name: 'message',
      label: 'Message',
      type: 'textarea'
    }
  ]
}
---
<h1>Contact Us!</h1>
<Form form={contactForm} />

Form Settings

Everying up to the

  • id*: This will be used to invoke the form.
  • action: A string. This value will fill the action attribute of the form. For built-in solutions, this is usually not required.
  • redirect (N,F,C): A string. A URL to which the form will redirect upon succes. Available on some solution.
  • submit_copy: A string. The copy of the submit button. Ex: Get in touch!
  • attributes: A map. A set of custom attributes to add to the form tag. Keys without values will be added as boolean attributes. Note that some attributes are added automatically (action, netlify-honeypot etc...) They will be overwritten by any honomymous attribute added here.
  • fields: An array. The list of fields to be printed among the forms.

Form Settings Example

  • title: Title of the form. (string)
  • action: Overwrite action value generated by the service logic. (string)
  • netlify: Netlify service configuration. (Service)
  • formspree: Formspree service configuration. (Service)
  • attributes: A map. A set of custom attributes to add to the input tag. Keys without values will be added as boolean attributes. Note that some attributes are added automatically (method, action etc...) They will be overwritten by any honomymous attribute added here.
  • method: Method used by the form. (string, default: 'POST', example: 'POST')
  • captcha: Use the service's captcha solution. (boolean, default: false)
  • cc: Send a copy of the submission to provided email(s). Only available on Formspree. (string)
  • redirect: Where to send the user after form submission. (string)
  • submitCopy: Customize the submit button's copy. (string, default: 'Submit', example: 'Sign up')
  • fields: Array of Field objects representing the form fields.
  • components: Override built-in label/wrapper/input components for this form or declare your own for custom input types. (object)

Fields Settings

A field is a group of HTML tags that consists of inputs, label. Some field can hold several inputs and label.

  • name: The name of the field (should be slug-like). (string)
  • type: The type of the field. (string, default: 'text', example: 'textarea')
  • value: Value for hidden fields. (string or number)
  • placeholder: Placeholder attribute for the field. (string)
  • hint: Smaller text below the field. (string)
  • attributes: A map. A set of custom attributes to add to the input tag. Keys without values will be added as boolean attributes. Note that some attributes are added automatically (name, id etc...) They will be overwritten by any honomymous attribute added here.
  • label: If set, used as a label. (string, default: name, example: 'Telephone')
  • required: Make the field 'required'. Label will be appended with '*'. (boolean)
  • options: Array of strings or objects for field options.
  • rows: Number of rows for textarea. (number)
  • columns: Reserved for group fields. (number)
  • fields: Array of nested fields for group fields.
  • components: Override built-in label/wrapper/input components for this field or declare your own for custom input types. (FormComponents)

Built in Field types

text|phone|email|file|textarea: Will use the same template with the defined type attribute

{
  name: 'name',
  label: 'Full Name',
  type: 'text'
}

select: A select field. Takes one extra key (options)

{
  name: 'state',
  label: 'State',
  type: 'select',
  options: [
    'Alabama',
    'Alaska',
    // ...
  ]
}

You can use an array of objects if sent values are different than the title.

{
  ...seeAbove,
  options: [
    { title: 'Alabama', value: 'AL' },
    { title: 'Alaska', value: 'AK' },
  ]
}

checkboxes|radios: A multiple choice field using either checkboxes or radios. Use radios type for radios and checkboxes for multiple checkboxes.

{
  name: 'source',
  label: 'How did you hear from us?',
  type: 'radios',
  options: [
    'A friend',
    'Internet',
    'Ad'
  ]
}

checkbox

A single checkbox field

{
  type: 'checkbox',
  name: 'agree',
  label: 'Do you agree with everything we will ever say?'
}

hidden

A hidden field

{
  type: 'hidden',
  name: 'lang_code',
  value: 'en'
}

In absence of the value setting, the module will use a hidden field's label setting for the value attribute.

Overriding Components

Every structural component can be replaced with your own Astro component via the components key, either at the form level (applies to all fields in the form) or at the field level (applies to that field only).

Submit button

Pass a custom submit component at the form level:

---
import MySubmit from './MySubmit.astro'

const form = {
  fields: [...],
  components: {
    submit: MySubmit
  }
}
---
<Form {form} />

Your component receives copy (the button label string) plus any extra props passed to <Form>:

---
// MySubmit.astro
const { copy } = Astro.props
---
<button type="submit" class="btn btn-primary">{copy}</button>

Field wrapper, label, and input

Override wrapper, label, and input components for all fields in a form:

---
import MyWrapper from './MyWrapper.astro'
import MyLabel from './MyLabel.astro'

const form = {
  fields: [...],
  components: {
    wrapper: MyWrapper,
    label: MyLabel,
  }
}
---

Override input for a specific field type across the whole form (e.g. replace all textareas):

components: {
  textarea: MyTextArea,
}

Override at the field level to target a single field only:

---
import MyInput from './MyInput.astro'

const form = {
  fields: [
    {
      name: 'email',
      type: 'email',
      components: {
        input: MyInput,
      }
    }
  ]
}
---

Custom wrapper components receive type, classes (array), and named slots label, input, and hint. Custom label and input components receive the full field object and classes (array).

Applying overrides to all forms

If you want the same component overrides across every form, wrap your form data with a helper:

import MySubmit from './MySubmit.astro'

const withOverrides = (form) => ({
  ...form,
  components: {
    submit: MySubmit,
    ...form.components,
  }
})
<Form form={withOverrides(contactForm)} />

Styling

The package ships with minimal default styles. The easiest way to customize them is via CSS custom properties, which should be declared on the form element or any ancestor (e.g. :root).

| Variable | Default | Controls | |---|---|---| | --tnd-forms-input-bg | white | Background color of all inputs and selects | | --tnd-forms-input-border | 1px solid currentColor | Border of all inputs, selects, and textareas | | --tnd-forms-input-padding | .5rem | Padding inside all inputs, selects, and textareas | | --tnd-forms-field-gap | 1rem | Vertical spacing between fields | | --tnd-forms-rounded | 3px | Border radius of inputs and the submit button |

Declare overrides in any global stylesheet:

:root {
  --tnd-forms-input-bg: #f9f9f9;
  --tnd-forms-input-border: 2px solid #ccc;
  --tnd-forms-input-padding: .75rem;
  --tnd-forms-field-gap: 1.5rem;
  --tnd-forms-rounded: 6px;
}

Or scope them to a specific form using the class passed via the classes prop:

.my-contact-form {
  --tnd-forms-input-bg: #fff8f0;
}
<Form form={contactForm} classes="my-contact-form" />