Complex Forms

What are Complex Forms?

Complex forms would typically contain data in a structure that is more than just simple key/value pairs. For example, a list of people who each have their own email, phone and street address. This would essentially be an array of objects.

Input naming


Nested items can be created by separating the items with . (dot) in the name. For example, would be converted to { person: { name: 'sam' } }.


Arrays, either top level or nested are created by specifying the zero-based index in the name. For example person.pets.0 would be converted to { person: { pets: [ 'cat' ] } }.


The key to creating a complex form is in the naming of the inputs. Below would be an example of the list described above:

  <input name="" value="Sam">
  <input name="" value="[email protected]">
  <input name="person.0.pets.0" value="cat">
  <input name="person.0.pets.1" value="dog">
  <input name="person.0.address.street" value="1234 Example Ave.">
  <input name="" value="Qwik">
  <input name="person.0.address.state" value="IA">
  <input name="" value="00000">
  <input name="person.0.pets.0" value="beaver">
  <input name="" value="Bonnie">
  <input name="" value="[email protected]">
  <input name="person.1.address.street" value="768 Resolution Way">
  <input name="" value="Jaffa">
  <input name="person.1.address.state" value="IL">
  <input name="" value="01948">

Output object

The after submitting the form the data would be parsed in an object like this:

  "person": [
      "name": "Sam",
      "email": "[email protected]",
      "address": {
        "street": "1234 Example Ave.",
        "city": "Qwik",
        "state": "IA",
        "zip": "00000"
      "pets": ["beaver"]
      "name": "Bonnie",
      "email": "[email protected]",
      "address": {
        "street": "768 Resolution Way",
        "city": "Jaffa",
        "state": "IL",
        "zip": "01948"

Using with Actions

Complex forms can be validated using zod$ with routeAction$ and globalAction$. Continuing with the previous examples it would look like this:

export const action = routeAction$(
  async (person) => {
    return { success: true, person, };
  // Zod schema is used to validate the FormData
    person: z.array(
        name: z.string(),
        email: z.string().email(),
        address: z.object({
          street: z.string(),
          city: z.string(),
          state: z.string(),
          zip: z.coerce.number()
        pets: z.array(z.string())


Thanks to all the contributors who have helped make this documentation better!

  • ulic75
  • hamatoyogi
  • aendel