# Number Field

Native numeric input with composable label and step controls.

- Package: `@mielui/svelte`
- Component version: `1.1.0`
- Depends on Mielui components: _internal/utils, button
- Shared utilities: components/_internal/button-attributes, hugeicons-icon, utils.cn, utils.createContext

## Install

~~~~sh
pnpm dlx @mielui/svelte add number-field
~~~~

## API

This reference is generated at build time from the component manifest, public `index.ts`, and documentation examples below. Changes to those source files are reflected here in the published Markdown. Standard Svelte and HTML attributes accepted by the exported prop types are supported.


### Decrement

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| onclick | ((event: MouseEvent) => void) \| undefined | - | No | No | Called on click, before the value steps. Call `preventDefault()` to skip the step. |
| element | HTMLButtonElement \| undefined | - | No | Yes | Bindable reference to the DOM element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Group

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Increment

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| onclick | ((event: MouseEvent) => void) \| undefined | - | No | No | Called on click, before the value steps. Call `preventDefault()` to skip the step. |
| element | HTMLButtonElement \| undefined | - | No | Yes | Bindable reference to the DOM element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Input

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| element | HTMLInputElement \| undefined | - | No | Yes | Bindable reference to the DOM element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Label

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Root

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| labels | NumberFieldLabels \| undefined | - | No | No | Overrides the built-in text and accessible names. Every key is optional and English is the fallback. |
| value | number \| undefined | - | No | Yes | Current number. Bindable. |
| inputId | string \| undefined | - | No | No | Id of the input, for a label outside the field. |
| min | number \| undefined | - | No | No | Smallest allowed value. |
| max | number \| undefined | - | No | No | Largest allowed value. |
| step | number \| undefined | 1 | No | No | Amount each step changes the value by. |
| name | string \| undefined | - | No | No | Field name submitted with the form. |
| form | string \| undefined | - | No | No | Id of the form this control belongs to when it sits outside that form. |
| disabled | boolean \| undefined | false | No | No | Prevents interaction and dims the control. |
| readonly | boolean \| undefined | false | No | No | Shows the value without allowing edits. The value is still submitted. |
| required | boolean \| undefined | false | No | No | Requires a value before the form can be submitted. |
| onValueChange | ((value: number \| undefined) => void) \| undefined | - | No | No | Called with the new value when it changes. |
| children | Snippet<[{ value: number \| undefined; }]> \| undefined | - | No | No | Content rendered inside. |
| element | HTMLDivElement \| undefined | - | No | Yes | Bindable reference to the DOM element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Public types

~~~~ts
import type { Snippet } from 'svelte';
import type {
    HTMLAttributes,
    HTMLButtonAttributes,
    HTMLInputAttributes,
    HTMLLabelAttributes
} from 'svelte/elements';
import Root from './number-field.svelte';
import Decrement from './number-field-decrement.svelte';
import Group from './number-field-group.svelte';
import Increment from './number-field-increment.svelte';
import Input from './number-field-input.svelte';
import Label from './number-field-label.svelte';

export type NumberFieldLabels = {
    /** Accessible name of the button that steps the value down. */
    decrease?: string;
    /** Accessible name of the button that steps the value up. */
    increase?: string;
};

export type NumberFieldProps = Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'onchange'> & {
    /**
     * Overrides the built-in text and accessible names. Every key is optional and English is the
     * fallback.
     */
    labels?: NumberFieldLabels;
    /** Current number. Bindable. */
    value?: number;
    /** Id of the input, for a label outside the field. */
    inputId?: string;
    /** Smallest allowed value. */
    min?: number;
    /** Largest allowed value. */
    max?: number;
    /** Amount each step changes the value by. */
    step?: number;
    /** Field name submitted with the form. */
    name?: string;
    /** Id of the form this control belongs to when it sits outside that form. */
    form?: string;
    /** Prevents interaction and dims the control. */
    disabled?: boolean;
    /** Shows the value without allowing edits. The value is still submitted. */
    readonly?: boolean;
    /** Requires a value before the form can be submitted. */
    required?: boolean;
    /** Called with the new value when it changes. */
    onValueChange?: (value: number | undefined) => void;
    /** Content rendered inside. */
    children?: Snippet<[{ value: number | undefined }]>;
    /** Bindable reference to the DOM element. */
    element?: HTMLDivElement | undefined;
};
export type NumberFieldInputProps = Omit<
    HTMLInputAttributes,
    'type' | 'value' | 'defaultValue' | 'min' | 'max' | 'step' | 'name' | 'form' | 'children'
> & {
    /** Bindable reference to the DOM element. */
    element?: HTMLInputElement | undefined;
};
export type NumberFieldLabelProps = HTMLLabelAttributes;
export type NumberFieldGroupProps = HTMLAttributes<HTMLDivElement>;
export type NumberFieldStepperProps = Omit<HTMLButtonAttributes, 'onclick'> & {
    /** Called on click, before the value steps. Call `preventDefault()` to skip the step. */
    onclick?: (event: MouseEvent) => void;
    /** Bindable reference to the DOM element. */
    element?: HTMLButtonElement | undefined;
};

export { Decrement, Group, Increment, Input, Label, Root };
~~~~

## Examples

### Decimal

~~~~svelte
<script lang="ts">
    import * as NumberField from '@mielui/svelte/components/number-field';

    let amount = $state<number | undefined>(1.5);
</script>

<NumberField.Root bind:value={amount} min={0} max={10} step={0.25} class="w-full max-w-56">
    <NumberField.Label>Hours</NumberField.Label>
    <NumberField.Group>
        <NumberField.Input class="text-left" />
        <NumberField.Decrement aria-label="Subtract fifteen minutes" />
        <NumberField.Increment aria-label="Add fifteen minutes" />
    </NumberField.Group>
</NumberField.Root>
~~~~

### Disabled

~~~~svelte
<script lang="ts">
    import * as NumberField from '@mielui/svelte/components/number-field';
</script>

<NumberField.Root value={4} disabled class="w-full max-w-56">
    <NumberField.Label>Included seats</NumberField.Label>
    <NumberField.Group>
        <NumberField.Decrement />
        <NumberField.Input />
        <NumberField.Increment />
    </NumberField.Group>
</NumberField.Root>
~~~~

### Form

~~~~svelte
<script lang="ts">
    import { Button } from '@mielui/svelte/components/button';
    import * as NumberField from '@mielui/svelte/components/number-field';

    let value = $state<number | undefined>(3);
    let submitted = $state('');
</script>

<form
    class="flex w-full max-w-64 flex-col gap-4"
    onsubmit={(event) => {
        event.preventDefault();
        submitted = String(new FormData(event.currentTarget).get('quantity'));
    }}
>
    <NumberField.Root name="quantity" bind:value min={1} max={20} required>
        <NumberField.Label>Quantity</NumberField.Label>
        <NumberField.Group>
            <NumberField.Decrement />
            <NumberField.Input />
            <NumberField.Increment />
        </NumberField.Group>
    </NumberField.Root>
    <div class="flex gap-2">
        <Button type="submit">Save quantity</Button>
        <Button type="reset" variant="outline">Reset</Button>
    </div>
    <p role="status" class="text-sm text-foreground-muted">
        {submitted
            ? `Saved quantity: ${submitted}`
            : 'Clear the input to see native required validation.'}
    </p>
</form>
~~~~

For the rendered reference, visit [/docs/components/number-field](/docs/components/number-field).
