Skip to content

Manual Usage

Most forms use PHP form classes and the generated <Form> component. For layout and presentation changes, start with Slots and Children, Styling, Customizing Presenters, or Custom Fields. Use this page for the remaining cases: placing package controls yourself, or rendering standalone controls outside an Inertia Forms form.

Manual rendering leaves the surrounding markup, layout, and any replaced behavior in your application.

There are two options:

  • Manual form rendering: keep the serialized PHP form config, call useForm, provide form context, and pass form.getField(...) into package fields.
  • Standalone components: import package fields directly and bind values with Vue v-model or React props. No PHP form config or package form provider is involved.

Import Paths

SurfaceVueReact
Form shell, useForm, FieldRenderer, shared helpers, and types@inertiaui/form-vue@inertiaui/form-react
Field components@inertiaui/form-vue/components@inertiaui/form-react/components
RichText@inertiaui/form-vue/richtext@inertiaui/form-react/richtext

Use the installation guide for runtime setup and styles. The source and generated TypeScript declarations for your installed version are the detailed reference.

Manual Form Rendering

You may render fields manually while keeping the backend form definition, validation, submission, and conditional visibility authoritative. Provide form context, then pass serialized field payloads from form.getField(...) to the components.

vue
<script setup>
import { provide } from 'vue'
import { FormContextKey, useForm } from '@inertiaui/form-vue'
import { TextInput } from '@inertiaui/form-vue/components'

const props = defineProps({ form: Object })
const form = useForm(props.form)

provide(FormContextKey, {
  formId: form.formId,
  data: form.data,
  rootData: form.data,
  errors: form.errors,
  processing: form.processing,
  setData: form.setData,
  validate: form.validate,
})
</script>

<template>
  <form @submit.prevent="form.submit()">
    <TextInput v-bind="form.getField('name')" />
    <TextInput v-bind="form.getField('email')" />

    <button type="submit" :disabled="form.processing.value">
      Save
    </button>
  </form>
</template>
tsx
import { FormContext, useForm } from '@inertiaui/form-react'
import { TextInput } from '@inertiaui/form-react/components'

export default function ManualProfileForm({ form: config }) {
    const form = useForm(config)

    const submit = (event) => {
        event.preventDefault()
        form.submit({ formElement: event.currentTarget })
    }

    return (
        <FormContext.Provider
            value={{
                formId: form.formId,
                data: form.data,
                rootData: form.data,
                errors: form.errors,
                rootErrors: form.errors,
                processing: form.processing,
                setData: form.setData,
                validate: form.validate,
            }}
        >
            <form onSubmit={submit}>
                <TextInput {...form.getField('name')} />
                <TextInput {...form.getField('email')} />

                <button type="submit" disabled={form.processing}>
                    Save
                </button>
            </form>
        </FormContext.Provider>
    )
}

useForm gives you the form data, errors, processing state, dirty tracking, validation, submission, and getField(name) helper. Vue exposes some values as refs or computed refs. React returns plain values.

Submitter Data In Manual Forms

The generated <Form> captures the clicked submit button automatically. Pass the native submitter from manual wrappers with named submit buttons.

vue
const submit = (event) => {
  form.submit({
    submitter: event.submitter instanceof HTMLElement ? event.submitter : null,
  })
}
tsx
const submit = (event) => {
    event.preventDefault()

    form.submit({
        formElement: event.currentTarget,
        submitter: event.nativeEvent.submitter instanceof HTMLElement
            ? event.nativeEvent.submitter
            : null,
    })
}

Only an enabled submit button or submit input with a name contributes submitter data.

Standalone Components

You may use standalone components for dialogs, filters, toolbars, local settings, or other UI that needs package controls without a generated PHP form payload. This uses the shipped presenters as-is. Use Customizing Presenters to copy or replace presenter markup. Standalone bindings win over inherited form context.

vue
<script setup>
import { ref } from 'vue'
import { TextInput, Toggle } from '@inertiaui/form-vue/components'

const title = ref('Launch notes')
const published = ref(true)
</script>

<template>
  <TextInput v-model="title" label="Title" name="title" />
  <Toggle v-model="published" label="Published" />
</template>
tsx
import { useState } from 'react'
import { TextInput, Toggle } from '@inertiaui/form-react/components'

export default function ArticleControls() {
    const [title, setTitle] = useState('Launch notes')
    const [published, setPublished] = useState(true)

    return (
        <>
            <TextInput value={title} onValueChange={setTitle} label="Title" name="title" />
            <Toggle checked={published} onCheckedChange={setPublished} label="Published" />
        </>
    )
}

Standalone name is optional. You may provide it for native form names or hidden inputs in a non-package form. Omit it for client-only controls.

Native Attributes And Events

Direct components forward common data-*, aria-*, autocomplete, keyboard, focus, blur, and change props to the stable interactive surface.

vue
<TextInput
  v-model="title"
  label="Title"
  data-testid="article-title"
  autocomplete="off"
  @blur="trackTitleBlur"
/>
tsx
<TextInput
    value={title}
    onValueChange={setTitle}
    label="Title"
    data-testid="article-title"
    autoComplete="off"
    onBlur={trackTitleBlur}
/>

Composite controls such as listboxes, popovers, uploads, editors, and collections own their internal ARIA and DOM structure. Use their documented props, classes, and partClasses instead of relying on internal nodes.

Standalone Uploads And Remote Controls

Most standalone inputs only need a value binding and display props. Remote, editor, upload, and composer controls need explicit endpoint or upload configuration because standalone mode does not inherit PHP form config:

  • RichText is imported from the richtext subpath. Image uploads need an explicit imageUploads config.
  • FileUpload needs explicit temporary, chunked, or direct-to-storage upload props when files are uploaded before final submit.
  • Composer writes plain text by default. Attachments require the same upload decisions as file uploads.
  • Combobox may use inline options. Remote search, record creation, and selected-value lookup need explicit source, selectedSource, or create props.

Rendering Serialized Fields

You may use FieldRenderer inside a package <Form> or manual FormContext provider to let the registry choose the presenter for one serialized field. Custom presenters and custom field registration are documented on Customizing Presenters and Custom Fields.