Forms
Vard Forms are initialized in code using the SDK. Once a form is registered, it appears in the Vard dashboard where clients can customize fields, reorder them, update labels, and configure submit behavior — no redeployments required.
The developer owns the form’s existence. The client owns its content.
How it works
- The developer registers a form in their codebase using the SDK (see embed options below). This creates the form and gives it a stable ID.
- The form appears in the Vard dashboard under Forms. Clients configure fields, submit settings, and success messages from there.
- When the client changes a field label or adds a new field, the live site reflects it within 60 seconds. No git commit. No deploy.
Getting the form ID
After registering a form in code, go to the Vard dashboard under Forms. Each form has a unique ID that looks like f47ac10b-58cc-4372-a567-0e02b2c3d479. Copy it from the embed snippet shown in the table, then use it in your embed.
Embedding the form
There are three ways to embed, depending on your stack. All three use the same form ID and hit the same API.
Script tag — plain HTML, no build step
The simplest option. Works in any HTML page, static site generator, or CMS that lets you add a <script> tag.
<script src="https://vard.app/dist/forms.iife.global.js" defer></script>
<div data-vard-form="f47ac10b-58cc-4372-a567-0e02b2c3d479"></div>The script scans for [data-vard-form] elements on DOMContentLoaded and mounts each one automatically. Multiple forms on the same page work — just add multiple data-vard-form divs.
ESM import — bundler-based projects (React, Vue, Svelte, Astro…)
import { VardForms } from "@vard-app/sdk/forms";
VardForms.mount("#contact-form", {
id: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
});<div id="contact-form"></div>mount() accepts either a CSS selector string or an HTMLElement reference.
Web component — declarative markup
If you prefer an HTML-first approach, the <vard-form> custom element works in any framework that supports web components (React 19+, Vue, Svelte, Lit, plain HTML).
<vard-form form-id="f47ac10b-58cc-4372-a567-0e02b2c3d479"></vard-form>Register the custom element before use:
import { VardForms } from "@vard-app/sdk/forms";
VardForms.defineWebComponent();Or use the script tag build — it registers <vard-form> automatically.
Styling
Forms render into the light DOM using structural class names. Your existing CSS applies directly — no shadow DOM, no CSS-in-JS, no specificity fights.
| Class | Element |
|---|---|
.vard-form | The <form> element |
.vard-field | Wrapper <div> around each field |
.vard-field--checkbox | Wrapper for checkbox fields |
.vard-label | <label> element |
.vard-input | <input>, <textarea>, or <select> |
.vard-input--checkbox | Checkbox <input> |
.vard-input--error | Added to inputs that fail validation |
.vard-submit | Submit <button> |
.vard-error | Per-field error message <span> |
.vard-error--global | Form-level error (e.g. network failure) |
.vard-success | Success message <p> shown after submit |
Example using Tailwind:
.vard-form { @apply flex flex-col gap-4; }
.vard-field { @apply flex flex-col gap-1.5; }
.vard-label { @apply text-sm font-medium text-foreground; }
.vard-input { @apply rounded-md border border-input bg-background px-3 py-2 text-sm; }
.vard-input--error { @apply border-destructive; }
.vard-submit { @apply rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground; }
.vard-error { @apply text-xs text-destructive; }
.vard-success { @apply text-sm text-foreground; }Framework examples
Next.js (App Router)
Forms are client-side by definition — they need DOM APIs. Use a client component wrapper.
"use client";
import { useEffect, useRef } from "react";
import { VardForms } from "@vard-app/sdk/forms";
export function ContactForm({ formId }: { formId: string }) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
if (ref.current) {
VardForms.mount(ref.current, { id: formId });
}
}, [formId]);
return <div ref={ref} />;
}import { ContactForm } from "@/components/contact-form";
export default function ContactPage() {
return (
<main>
<h1>Contact us</h1>
<ContactForm formId="f47ac10b-58cc-4372-a567-0e02b2c3d479" />
</main>
);
}Astro
---
// No imports needed — script tag handles it
---
<html>
<body>
<h1>Contact us</h1>
<div data-vard-form="f47ac10b-58cc-4372-a567-0e02b2c3d479"></div>
<script src="https://vard.app/dist/forms.iife.global.js" defer></script>
</body>
</html>Vue
<template>
<div ref="container" />
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { VardForms } from "@vard-app/sdk/forms";
const props = defineProps<{ formId: string }>();
const container = ref<HTMLElement | null>(null);
onMounted(() => {
if (container.value) {
VardForms.mount(container.value, { id: props.formId });
}
});
</script>Submit behavior
Configure how submissions are handled from the Vard dashboard under the form’s Submit settings:
| Setting | Description |
|---|---|
| Store submissions | Always on. Every submission is saved and viewable in the dashboard. |
| Email notification | Send an email to a specified address on each submission. |
| Webhook | POST submission data as JSON to any URL (Zapier, Make, custom endpoint). |
| Success message | Text shown in place of the form after a successful submit. |
| Success redirect | URL to redirect to instead of showing the success message. |
VardForms.mount() options
VardForms.mount(target, {
// Required — the form ID from the Vard dashboard
id: string;
// Optional — override the Vard API host (useful for self-hosting or local dev)
apiHost?: string;
// Optional — called after the form config is fetched and rendered
onSuccess?: (config: FormConfig) => void;
// Optional — called if the config fetch fails
onError?: (error: Error) => void;
})Field types
Clients can configure any of these field types from the dashboard:
| Type | Input rendered |
|---|---|
text | Single-line text input |
email | Email input (validated on submit) |
phone | Tel input |
number | Number input |
textarea | Multi-line text area (4 rows) |
select | Dropdown with client-defined options |
checkbox | Single checkbox (e.g. “I agree to the privacy policy”) |
Validation
Validation runs server-side on every submission. Required fields are enforced, email format is checked, and select values are validated against the configured options. Per-field error messages are returned and displayed inline next to the relevant input.
Client-side HTML5 validation is intentionally disabled (novalidate) so that all error messages come from the server and stay consistent regardless of browser.