Part of the lingo docs (natural-language quantities, units, dates parser) — index: https://lingo.pascal.app/llms.txt

## Frameworks

One field, every framework: React Hook Form, TanStack Form, Formik, Vue, Angular, shadcn/ui, vanilla, and a `<lingo-input>` web component.

The docs show one live preview field, then a grouped integration picker with actual SVG brand logos and brand-color accents for each snippet. Vanilla, React, Next.js, React Hook Form, TanStack Form, shadcn/ui, Vue, Svelte, Node, agents, and the web component all feed the same field contract.

The form-library snippets use the same field contract: `standardSchemaResolver(lingoObject(shape, { passthrough: true }))` for whole-form React Hook Form and shadcn/ui forms, `validators.onChange` plus a `useLingoInput` bridge for TanStack Form, and `defineLingoInput()` for the form-associated `<lingo-input>` element.

Caption: The code swaps adapters; the field contract stays the same.

### Vanilla

~~~html
<form method="post" action="/signup" novalidate>
  <label for="height">Height</label>
  <input
    id="height"
    name="height"
    inputmode="text"
    placeholder="5'11&quot; or 180cm"
    required
  />
  <p id="height-error" role="alert"></p>
  <p id="height-hint" aria-hidden="true"></p>
  <button>Continue</button>
</form>

<script type="module">
  import { lingoInput } from "@pascal-app/lingo/dom"

  lingoInput(document.querySelector("#height"), {
    kind: "length",
    unit: "m",
    name: "height",
    min: "0.3m",
    max: "2.5m",
    required: true,
    validationBehavior: "aria",
    errorElement: "#height-error",
    hintElement: "#height-hint",
  })
</script>
~~~

### React

~~~tsx
import { useLingoInput } from "@pascal-app/lingo/react"

function HeightField() {
  const field = useLingoInput({
    kind: "length",
    unit: "m",
    name: "height_m",
  })

  return <input ref={field.ref} placeholder="5'11\" or 180cm" />
}
~~~

### React Hook Form

~~~tsx
import {
  standardSchemaResolver,
} from "@hookform/resolvers/standard-schema"
import { useForm } from "react-hook-form"
import {
  dateField,
  lingoObject,
  quantityField,
} from "@pascal-app/lingo/ai"

const shape = {
  weight_kg: quantityField({
    kind: "mass",
    unit: "kg",
    min: 0,
  }),
  visit_date: dateField({ now: new Date() }),
}

const form = useForm({
  resolver: standardSchemaResolver(
    lingoObject(shape, { passthrough: true }),
  ),
})

form.handleSubmit((data) => {
  // data.weight_kg is a number in kg.
  // data.visit_date is an ISO string.
})
~~~

### TanStack

~~~tsx
import { quantityField } from "@pascal-app/lingo/ai"
import { useLingoInput } from "@pascal-app/lingo/react"

const heightField = quantityField({
  kind: "length",
  unit: "m",
  min: 0.3,
  max: 2.5,
})

function HeightBridge({ field }) {
  const { ref, state } = useLingoInput({
    kind: "length",
    unit: "m",
    value: field.state.value ?? null,
    onValueChange: (value) => field.handleChange(value),
  })
  return (
    <input
      ref={ref}
      data-state={state}
      onBlur={field.handleBlur}
    />
  )
}

<form.Field
  name="height_m"
  validators={{ onChange: heightField }}
>
  {(field) => <HeightBridge field={field} />}
</form.Field>
~~~

### shadcn/ui

~~~tsx
import {
  standardSchemaResolver,
} from "@hookform/resolvers/standard-schema"
import { Controller, useForm } from "react-hook-form"
import {
  lingoObject,
  quantityField,
} from "@pascal-app/lingo/ai"
import {
  Field,
  FieldDescription,
  FieldError,
  FieldLabel,
} from "@/components/ui/field"
import { Input } from "@/components/ui/input"

const schema = lingoObject({
  height: quantityField({
    kind: "length",
    unit: "m",
    min: 0.3,
    max: 2.5,
  }),
})
const form = useForm({
  resolver: standardSchemaResolver(schema),
  defaultValues: { height: "" },
})

<Controller
  name="height"
  control={form.control}
  render={({ field, fieldState }) => (
    <Field data-invalid={fieldState.invalid}>
      <FieldLabel htmlFor={field.name}>Height</FieldLabel>
      <Input
        {...field}
        id={field.name}
        aria-invalid={fieldState.invalid}
        placeholder="5'11&quot; or 180cm"
      />
      <FieldDescription>
        Any format - imperial or metric.
      </FieldDescription>
      {fieldState.invalid ? (
        <FieldError errors={[fieldState.error]} />
      ) : null}
    </Field>
  )}
/>
~~~

### Web component

~~~html
<script type="module">
  import { defineLingoInput } from "@pascal-app/lingo/element"

  defineLingoInput()
</script>

<form>
  <label for="height">Height</label>
  <lingo-input
    id="height"
    name="height_m"
    kind="length"
    unit="m"
  ></lingo-input>
</form>
<!-- after committing 5'11", -->
<!-- the form submits height_m=1.8034 -->
~~~

### Next action

~~~ts
"use server"

import { parseQuantity } from "@pascal-app/lingo"

export async function validateLength(_prev, formData) {
  const input = String(formData.get("length") ?? "")
  const result = parseQuantity(input, {
    kind: "length",
    unit: "m",
    strictness: "confirm",
    accept: { ranges: false, conversions: false },
  })

  if (!result.ok) {
    return { ok: false, issues: result.issues }
  }

  return {
    ok: true,
    canonicalMeters: result.quantity.base,
    formatted: result.quantity.format({ unit: "m" }),
  }
}
~~~

### Vue

~~~vue
<script setup>
import { onBeforeUnmount, onMounted, ref } from "vue"
import { lingoInput } from "@pascal-app/lingo/dom"

const el = ref(null)
let field

onMounted(() => {
  field = lingoInput(el.value, {
    kind: "mass",
    unit: "kg",
    name: "weight_kg",
  })
})

onBeforeUnmount(() => field?.destroy())
</script>

<template>
  <input ref="el" placeholder="2 lb 3 oz" />
</template>
~~~

### Svelte

~~~svelte
<script>
  import { lingoInput } from "@pascal-app/lingo/dom"

  export function lingo(node, options) {
    const field = lingoInput(node, options)
    return {
      update(next) { field.update(next) },
      destroy() { field.destroy() },
    }
  }
</script>

<input use:lingo={{ kind: "length", unit: "m", name: "height_m" }} />
~~~

### Node

~~~ts
import { parseQuantity } from "@pascal-app/lingo"

export function validatePayload(payload) {
  const result = parseQuantity(payload.length, {
    kind: "length",
    unit: "m",
    strictness: "confirm",
    accept: { ranges: false, conversions: false },
  })

  if (!result.ok) {
    return {
      ok: false,
      issues: result.issues,
      candidate: result.candidate,
    }
  }

  return { ok: true, meters: result.quantity.base }
}
~~~

### Agents

~~~js
// llms.txt tells agents to pass strings, not floats.
// A browser automation agent can fill the field either way:

lingoInput.get(document.querySelector("#height"))?.set("5'11\"")

// Or it can type natural language. The hidden input named height_m
// carries the canonical value for normal form submission.
~~~

Human docs: https://lingo.pascal.app/docs#integrations