Forms - v1.1.3

Vue Components

Vue 3 components for rendering and managing ArtisanPack UI Forms using the Composition API.

FormRenderer

The main component for displaying forms to users. Fetches the form definition from the REST API and handles the full submission lifecycle.

Props

Prop Type Required Default Description
baseUrl string Yes - API base URL (e.g., /api/v1/forms)
formSlug string Yes - Form slug or ID
clientValidation boolean No true Enable client-side validation
formClass string No - CSS class for form wrapper

Events

Event Payload Description
success SubmitFormResponse Emitted after successful submission
error Error Emitted on submission error

Slots

Slot Props Description
#loading - Custom loading skeleton
#error { message: string } Custom error display
#success { message: string } Custom success message

Usage

<script setup lang="ts">
import { FormRenderer } from './vendor/artisanpack-forms/vue';
</script>

<template>
    <!-- Basic usage -->
    <FormRenderer
        base-url="/api/v1/forms"
        form-slug="contact-us"
    />

    <!-- With events and custom class -->
    <FormRenderer
        base-url="/api/v1/forms"
        form-slug="contact-us"
        form-class="max-w-lg mx-auto"
        @success="handleSuccess"
        @error="handleError"
    />

    <!-- With custom slots -->
    <FormRenderer
        base-url="/api/v1/forms"
        form-slug="contact-us"
    >
        <template #loading>
            <MySpinner />
        </template>

        <template #error="{ message }">
            <MyErrorBanner :message="message" />
        </template>

        <template #success="{ message }">
            <MySuccessPage :message="message" />
        </template>
    </FormRenderer>
</template>

useForm Composable

Manages form state, validation, step navigation, file uploads, and submission. Returns reactive refs.

Return Value

const {
    form,               // Ref<FormRenderData | null>
    values,             // Ref<Record<string, unknown>>
    errors,             // Ref<Record<string, string[]>>
    hiddenFields,       // Ref<Record<string, boolean>>
    isLoading,          // Ref<boolean>
    isSubmitting,       // Ref<boolean>
    isSubmitted,        // Ref<boolean>
    currentStep,        // Ref<number> (0-based index)
    totalSteps,         // Ref<number>
    progressPercentage, // Ref<number> (0-100)
    currentFields,      // Ref<FormField[]>
    currentStepData,    // Ref<FormStep | null>
    loadError,          // Ref<string | null>
    setValue,           // (field: string, value: unknown) => void
    setFile,            // (field: string, file: File | File[]) => void
    nextStep,           // () => Promise<void>
    prevStep,           // () => void
    goToStep,           // (step: number) => Promise<void>
    submit,             // () => Promise<void>
    reset,              // () => void
} = useForm({ baseUrl, formSlug, clientValidation, onSuccess, onError });

Usage

<script setup lang="ts">
import { useForm } from './vendor/artisanpack-forms/vue';

const {
    form, values, errors, isLoading, isSubmitting,
    isSubmitted, setValue, submit
} = useForm({
    baseUrl: '/api/v1/forms',
    formSlug: 'contact',
    onSuccess: (res) => console.log('Done!', res),
});
</script>

<template>
    <div v-if="isLoading">Loading...</div>
    <div v-else-if="isSubmitted">{{ form?.success_message }}</div>
    <form v-else @submit.prevent="submit">
        <div v-for="field in form?.fields" :key="field.id">
            <label>{{ field.label }}</label>
            <input
                :value="values[field.name]"
                @input="setValue(field.name, ($event.target as HTMLInputElement).value)"
            />
            <span
                v-for="(err, i) in errors[field.name]"
                :key="i"
                class="text-red-500"
            >
                {{ err }}
            </span>
        </div>
        <button type="submit" :disabled="isSubmitting">
            {{ isSubmitting ? 'Submitting...' : form?.submit_button_text }}
        </button>
    </form>
</template>

useApi Composable

Type-safe API client for communicating with the Forms REST API.

import { useApi, ApiError, ApiValidationError } from './vendor/artisanpack-forms/vue';

const api = useApi({ baseUrl: '/api/v1/forms' });

try {
    const form = await api.get('/contact/render');
    const result = await api.post('/contact/submit', formData);
} catch (error) {
    if (error instanceof ApiValidationError) {
        console.log(error.errors); // { field: ['message'] }
    } else if (error instanceof ApiError) {
        console.log(error.status, error.message);
    }
}

useAutoSave Composable

Automatically saves form drafts to prevent data loss.

import { useAutoSave } from './vendor/artisanpack-forms/vue';

useAutoSave({
    key: 'contact-form-draft',
    data: formValues,
    interval: 5000, // Save every 5 seconds
});

Field Components

Individual field type components for building custom form layouts:

Component Field Types
TextField text, email, phone, number, url, textarea, hidden, time
ChoiceField select, select_multiple, radio, checkbox, checkbox_group
AdvancedField file, date
LayoutField heading, paragraph, divider, html
FieldRenderer Renders any field by type (delegates to the above)

HoneypotField

Dedicated component for rendering the hidden honeypot spam protection field:

<HoneypotField :field-name="form.config.honeypot.field_name" />

Admin Components

Full admin management components for building form administration interfaces:

Component Description
FormsList List and manage all forms
FormBuilder Drag-and-drop form builder
FieldEditor Edit individual field properties
FieldPalette Field type picker for the builder
ConditionalLogicEditor Configure field visibility rules
NotificationEditor Manage email notifications
SubmissionsList View and manage submissions
SubmissionDetail View a single submission
<script setup lang="ts">
import {
    FormsList,
    FormBuilder,
    SubmissionsList,
    SubmissionDetail,
} from './vendor/artisanpack-forms/vue';
</script>

<template>
    <!-- Forms management page -->
    <FormsList base-url="/api/v1/forms" />

    <!-- Form builder -->
    <FormBuilder base-url="/api/v1/forms" :form-id="1" />

    <!-- Submissions list -->
    <SubmissionsList base-url="/api/v1/forms" :form-id="1" />

    <!-- Submission detail -->
    <SubmissionDetail base-url="/api/v1/forms" :form-id="1" :submission-id="42" />
</template>

Next Steps