X Enterprises

nuxt-x-app-formkit

FormKit-powered form containers with modal and slideover variants, plus a drag-and-drop file upload component.

nuxt-x-app-formkit

FormKit-powered form containers with modal and slideover variants, plus a drag-and-drop file upload component. All form containers support both a CRUD mode (pass an endpoint prop and the component handles fetch, submit, validation, and toast notifications automatically) and a legacy mode (omit endpoint and handle submission yourself via the @submit event).

Installation

npm install @xenterprises/nuxt-x-formkit
// nuxt.config.ts
export default defineNuxtConfig({
  extends: ['@xenterprises/nuxt-x-formkit']
})

Components

ComponentDescription
XFormModalFormKit form in a UModal overlay
XFormSlideFormKit form in a USlideover panel
XFormFileUploadDrag-and-drop file picker with validation

XFormModal

A modal dialog wrapping a FormKit form. In CRUD mode (endpoint provided) the component fetches an existing record by recordId, runs FormKit validation on submit, posts/puts to the API, shows a toast, and optionally refreshes or navigates on success.

Props

PropTypeDefaultDescription
modelValuebooleanfalseControls modal open state (v-model)
titlestring""Modal header title. Auto-derived from endpoint when omitted
size"sm" | "md" | "lg" | "xl" | "full""md"Max-width of the modal content
closablebooleantrueShow the X close button in the header
loadingbooleanfalseExternal loading state merged into button disabled/spinner
disabledbooleanfalseDisable the submit button
submitTextstring"Save"Submit button label (auto becomes "Create" / "Update" in CRUD mode)
cancelTextstring"Cancel"Cancel button label
confirmClosebooleanfalseSuppress auto-close on X click; handle @close yourself
endpointstringAPI base path (e.g. /api/users). Enables CRUD mode
recordIdstring | numberID of the record to fetch and edit
initialDataRecord<string, any>Pre-populate form without fetching from API
method"POST" | "PUT" | "PATCH"autoHTTP method override (defaults to POST for create, PUT for edit)
idKeystring"id"Key used to detect and append record ID to the URL
headersRecord<string,string> | () => Record<string,string>Extra request headers, or a function returning them
transform(data) => dataTransform form data before it is sent to the API
showToastbooleantrueShow success/error toasts via useToast()
closeOnSuccessbooleantrueClose modal after a successful API call
resetOnClosebooleantrueClear form data when the modal closes
formIdstringautoOverride the generated FormKit form node ID
onSuccess"refresh" | "navigate" | FunctionPost-success action: refresh Nuxt data, navigate to a route, or custom callback
navigateTostringRoute template for onSuccess: "navigate" (supports :id / {id})

Emits

EventPayloadDescription
update:modelValuebooleanv-model sync
submitFired in legacy mode (no endpoint) when the submit button is clicked
closeFired when the X or Cancel button is clicked
success(data, response)Fired after a successful API call
error(error)Fired when the API call fails
loaded(data)Fired after the record is fetched by recordId

Exposed Methods

MethodDescription
open(data?)Open the modal and optionally pre-populate form data
close()Close the modal
reset()Re-initialize form data
submit()Programmatically trigger form submission

Usage

CRUD mode — create a new user:

<template>
  <UButton @click="open = true">New User</UButton>

  <XFormModal
    v-model="open"
    endpoint="/api/users"
    :on-success="'refresh'"
  >
    <template #default="{ isEdit }">
      <FormKitText name="name" label="Name" validation="required" />
      <FormKitText name="email" label="Email" validation="required|email" />
    </template>
  </XFormModal>
</template>

<script setup>
const open = ref(false)
</script>

CRUD mode — edit an existing record:

<XFormModal
  v-model="open"
  endpoint="/api/users"
  :record-id="user.id"
  on-success="refresh"
/>

Legacy mode — handle submit yourself:

<XFormModal v-model="open" title="Custom Form" @submit="handleSubmit">
  <input v-model="form.name" />
</XFormModal>

XFormSlide

Identical feature set to XFormModal but rendered as a USlideover panel. Supports the same CRUD props and success behaviors.

Props

PropTypeDefaultDescription
modelValuebooleanfalseControls slideover open state (v-model)
titlestring""Panel header title. Auto-derived from endpoint when omitted
side"left" | "right""right"Which edge the panel slides from
size"sm" | "md" | "lg" | "xl""md"Panel width (sm=320px, md=400px, lg=500px, xl=600px)
loadingbooleanfalseExternal loading state
disabledbooleanfalseDisable the submit button
submitTextstring"Save"Submit button label
cancelTextstring"Cancel"Cancel button label
confirmClosebooleanfalseSuppress auto-close; handle @close yourself
endpointstringAPI base path. Enables CRUD mode
recordIdstring | numberID of the record to fetch and edit
initialDataRecord<string, any>Pre-populate form without fetching from API
method"POST" | "PUT" | "PATCH"autoHTTP method override
idKeystring"id"Key used to detect and append record ID
headersRecord<string,string> | () => Record<string,string>Extra request headers
transform(data) => dataTransform form data before sending
showToastbooleantrueShow success/error toasts
closeOnSuccessbooleantrueClose panel after successful API call
resetOnClosebooleantrueClear form data when the panel closes
formIdstringautoOverride the generated FormKit form node ID
onSuccess"refresh" | "navigate" | FunctionPost-success action
navigateTostringRoute template for onSuccess: "navigate"

Emits

EventPayloadDescription
update:modelValuebooleanv-model sync
submitFired in legacy mode
closeFired when closed
success(data, response)Fired after successful API call
error(error)Fired on API error
loaded(data)Fired after record fetch

Exposed Methods

MethodDescription
open(data?)Open the panel, optionally pre-populate
close()Close the panel
reset()Re-initialize form data
submit()Programmatically trigger submission

Usage

<template>
  <UButton @click="slide = true">Edit Profile</UButton>

  <XFormSlide
    v-model="slide"
    endpoint="/api/profile"
    :record-id="user.id"
    side="right"
    size="lg"
    on-success="refresh"
  >
    <FormKitText name="displayName" label="Display Name" validation="required" />
    <FormKitText name="bio" label="Bio" />
  </XFormSlide>
</template>

<script setup>
const slide = ref(false)
</script>

XFormFileUpload

A drag-and-drop file upload zone. Emits selected files as a File or File[] via v-model. Optionally calls an async uploadHandler immediately after files are selected.

Props

PropTypeDefaultDescription
modelValueFile | File[] | nullnullSelected file(s) (v-model)
multiplebooleanfalseAllow multiple file selection
acceptstring"*"Native accept attribute passed to <input type="file">
acceptLabelstring""Human-readable label shown beneath the upload icon (falls back to accept value)
maxSizenumberMaximum file size in bytes; emits @error if exceeded
maxFilesnumberMaximum number of files when multiple is true
disabledbooleanfalseDisable all interactions
dropzonebooleantrueEnable drag-and-drop
uploadHandler(files: File[]) => Promise<void>Called automatically after files are selected; shows uploading spinner

Emits

EventPayloadDescription
update:modelValueFile | File[] | nullv-model sync
errorstringValidation error message (size exceeded, too many files, upload failure)

Exposed Methods

MethodDescription
clear()Remove all selected files
openFilePicker()Programmatically open the OS file picker

Usage

Basic single file:

<template>
  <XFormFileUpload
    v-model="file"
    accept="image/*"
    accept-label="PNG, JPG, GIF up to 5 MB"
    :max-size="5 * 1024 * 1024"
    @error="toast.add({ title: $event, color: 'error' })"
  />
</template>

<script setup>
const file = ref(null)
</script>

Multiple files with upload handler:

<XFormFileUpload
  v-model="files"
  multiple
  accept=".pdf,.docx"
  :max-files="5"
  :upload-handler="uploadToS3"
/>

AI Context

package: "@xenterprises/nuxt-x-formkit"
version: "0.1.0"
components:
  - XFormModal
  - XFormSlide
  - XFormFileUpload
crud-mode: pass `endpoint` prop; component handles fetch/submit/toast/close automatically
legacy-mode: omit `endpoint`; handle @submit event yourself
success-behaviors: onSuccess="refresh" | "navigate" | custom function
form-engine: FormKit (validation, node.submit(), node.setErrors())
ui-primitives: UModal, USlideover (Nuxt UI)
file-upload: v-model File | File[]; optional uploadHandler async function
common-pattern: |
  <XFormModal v-model="open" endpoint="/api/resource" :record-id="id" on-success="refresh">
    <FormKitText name="field" label="Field" validation="required" />
  </XFormModal>
Copyright © 2026