HUGE can build forms from a few config lines
// astro.config.mjs
import tndForms from "./src/packages/forms"
export default defineConfig({
site: 'https://something.com',
intergrations: [
tndForms({
submitCopy: 'Get in touch',
})
]
})---
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} />Everying up to the
- id*: This will be used to invoke the form.
- action: A string. This value will fill the
actionattribute 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-honeypotetc...) They will be overwritten by any honomymous attribute added here. - fields: An array. The list of fields to be printed among the forms.
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,actionetc...) 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)
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,idetc...) 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)
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.
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).
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>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).
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)} />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" />