@usevyre/react@usevyre/vue

Field

A form-field wrapper for labels, descriptions, validation messages, and layout. Use the props-based API for the common case, or thecomposable parts (FieldLabel,FieldDescription, FieldError,FieldGroup, FieldSet) for richer layouts. Both APIs work together — the props-based form is unchanged and fully supported.


Props-based

The simplest form: pass label, hint,state, and required. The control goes in the default slot.

We'll never share it.
import { Field, Input } from "@usevyre/react";

<Field label="Email" hint="We'll never share it." required>
  <Input type="email" placeholder="you@example.com" />
</Field>

Composable

Compose a field from explicit parts when you need custom message placement or conditional error vs. description. FieldError renders nothing when it has no children, so you can keep it always mounted.

Used for sign-in and receipts.

import {
  Field, FieldLabel, FieldDescription, FieldError, Input,
} from "@usevyre/react";

const [val, setVal] = useState("");
const invalid = val.length > 0 && !val.includes("@");

<Field state={invalid ? "error" : "idle"}>
  <FieldLabel required htmlFor="email">Email</FieldLabel>
  <Input
    id="email"
    type="email"
    value={val}
    onChange={(e) => setVal(e.target.value)}
  />
  {invalid ? (
    <FieldError>Enter a valid email address.</FieldError>
  ) : (
    <FieldDescription>Used for sign-in and receipts.</FieldDescription>
  )}
</Field>

Field group

Wrap multiple fields in FieldGroup to lay them out horizontally (equal columns) or vertically.

import { FieldGroup, Field, Input } from "@usevyre/react";

<FieldGroup orientation="horizontal">
  <Field label="First name"><Input placeholder="Ada" /></Field>
  <Field label="Last name"><Input placeholder="Lovelace" /></Field>
</FieldGroup>

Complete form

A real registration form composing many fields together —FieldGroup for the name row, validated Inputwith conditional FieldError, a RadioGroup for the plan, a Textarea bio, a FieldSet for grouped notification options, and a Checkbox with an inline label. Everything is controlled state — submit is disabled until it's valid.

Used for sign-in and receipts.

Markdown is supported.

Notifications
import { useState } from "react";
import {
  Field, FieldLabel, FieldDescription, FieldError, FieldGroup, FieldSet,
  Input, Textarea, Checkbox, RadioGroup, Button,
} from "@usevyre/react";

const [form, setForm] = useState({
  name: "", email: "", plan: "pro", bio: "", notify: "email", terms: false,
});
const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));
const emailInvalid = form.email.length > 0 && !form.email.includes("@");

<form onSubmit={handleSubmit}>
  <FieldGroup orientation="horizontal">
    <Field label="First name" required>
      <Input value={form.name} onChange={(e) => set("name", e.target.value)} />
    </Field>
    <Field label="Last name">
      <Input placeholder="Lovelace" />
    </Field>
  </FieldGroup>

  <Field state={emailInvalid ? "error" : "idle"}>
    <FieldLabel required htmlFor="email">Email</FieldLabel>
    <Input
      id="email"
      type="email"
      value={form.email}
      onChange={(e) => set("email", e.target.value)}
    />
    {emailInvalid ? (
      <FieldError>Enter a valid email address.</FieldError>
    ) : (
      <FieldDescription>Used for sign-in and receipts.</FieldDescription>
    )}
  </Field>

  <Field>
    <FieldLabel>Plan</FieldLabel>
    <RadioGroup
      value={form.plan}
      onChange={(v) => set("plan", v)}
      options={[
        { value: "free", label: "Free", description: "For hobby projects" },
        { value: "pro",  label: "Pro",  description: "For small teams" },
        { value: "ent",  label: "Enterprise", description: "SSO, audit log, SLA" },
      ]}
    />
  </Field>

  <Field>
    <FieldLabel htmlFor="bio">Bio</FieldLabel>
    <Textarea
      id="bio"
      rows={3}
      value={form.bio}
      onChange={(e) => set("bio", e.target.value)}
    />
    <FieldDescription>Markdown is supported.</FieldDescription>
  </Field>

  <FieldSet legend="Notifications">
    <RadioGroup
      value={form.notify}
      onChange={(v) => set("notify", v)}
      orientation="horizontal"
      options={[
        { value: "email", label: "Email" },
        { value: "sms",   label: "SMS" },
        { value: "none",  label: "None" },
      ]}
    />
  </FieldSet>

  <Field>
    <div style={{ display: "flex", alignItems: "flex-start", gap: 8 }}>
      <Checkbox
        id="terms"
        checked={form.terms}
        onCheckedChange={(c) => set("terms", c)}
      />
      <label htmlFor="terms">I accept the terms and conditions</label>
    </div>
  </Field>

  <Button type="submit" variant="primary">Create account</Button>
</form>

Field props

Props

PropTypeDefaultDescription
labelstringVisible field label (props-based API).
hintstringHelper text below the control. Turns into an error message when state="error".
state"idle" | "error" | "success" | "warning""idle"Validation state — also styles the hint.
requiredbooleanfalseAppends a required asterisk to the label.
htmlFor / forstringLinks the label to a control id.
className / classstringAdditional CSS class.

Composable parts

Props

PropTypeDefaultDescription
FieldLabelrequired?: boolean, htmlFor/forExplicit label; required adds the asterisk.
FieldDescriptionMuted helper text below the control.
FieldErrorError message (role="alert"); renders nothing when empty.
FieldGrouporientation?: "vertical" | "horizontal""vertical"Lays out multiple fields/controls in a row or column.
FieldSetlegend?: stringSemantic <fieldset> with optional <legend> for grouped inputs.