> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Number Field – Code

> Number Field is the standard component for accepting numerical input, featuring optional increment/decrement buttons and configurable step values.

export const LiveCode = ({children, customHeight, clickToLoad, example, fullWidth, fullHeight, hideCodeInLiveCode, screenshot, screenshotOnly, showCode: showCodeProp}) => {
  const SCREENSHOTS_BASE = "https://servicetitan.github.io/anvil2-docs-live-code/screenshots";
  const STACKBLITZ_BASE = "https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples";
  const [showCodeBlock, setShowCodeBlock] = useState(showCodeProp ?? false);
  const [isLocalOverride, setIsLocalOverride] = useState(false);
  useEffect(() => {
    const examplePath = `/images/live-code-screenshots-tmp/${example}.png`;
    fetch(examplePath, {
      method: "HEAD"
    }).then(r => {
      if (r.ok) setIsLocalOverride(true);
    }).catch(() => {});
  }, [example]);
  const screenshotBase = isLocalOverride ? "/images/live-code-screenshots-tmp" : SCREENSHOTS_BASE;
  if (screenshotOnly) {
    return <Frame className="flex flex-col">
        <div className="flex dark:hidden" style={{
      justifyContent: "center",
      alignItems: "center",
      width: fullWidth ? "100%" : "50%",
      minHeight: fullHeight ? "284px" : undefined,
      background: "#FFFFFF"
    }}>
          <img srcset={`${screenshotBase}/${example}.png, ${screenshotBase}/${example}-2x.png 2x`} src={`${screenshotBase}/${example}.png`} alt={example} noZoom />
        </div>
        <div className="hidden dark:flex" style={{
      justifyContent: "center",
      alignItems: "center",
      width: fullWidth ? "100%" : "50%",
      minHeight: fullHeight ? "284px" : undefined,
      background: "#141414"
    }}>
          <img srcset={`${screenshotBase}/${example}-dark.png, ${screenshotBase}/${example}-dark-2x.png 2x`} src={`${screenshotBase}/${example}-dark.png`} alt={example} noZoom />
        </div>
      </Frame>;
  }
  if (screenshot) {
    return <Frame className="flex flex-col -mb-2">
        <div className="flex dark:hidden bg-white dark:bg-codeblock border border-gray-950/10 dark:border-white/10 dark:twoslash-dark rounded-2xl overflow-hidden" style={{
      justifyContent: "center",
      alignItems: "center",
      width: fullWidth ? "100%" : "50%",
      minHeight: fullHeight ? "284px" : undefined
    }}>
          <img srcset={`${screenshotBase}/${example}.png, ${screenshotBase}/${example}-2x.png 2x`} src={`${screenshotBase}/${example}.png`} alt={example} noZoom />
        </div>

        <div className="hidden dark:flex bg-white dark:bg-codeblock border border-gray-950/10 dark:border-white/10 dark:twoslash-dark rounded-2xl overflow-hidden" style={{
      background: "#141414",
      justifyContent: "center",
      alignItems: "center",
      width: fullWidth ? "100%" : "50%",
      minHeight: fullHeight ? "284px" : undefined
    }}>
          <img srcset={`${screenshotBase}/${example}-dark.png, ${screenshotBase}/${example}-dark-2x.png 2x`} src={`${screenshotBase}/${example}-dark.png`} alt={example} noZoom />
        </div>

        <div className="flex justify-end items-center text-xs py-2 px-1 gap-4">
          {!showCodeProp ? <button className="inline-flex justify-end items-center text-gray-700 dark:text-gray-50 hover:text-blue-500 dark:hover:text-blue-300 transition-colors group self-end gap-1 cursor-pointer" onClick={() => setShowCodeBlock(!showCodeBlock)} style={{
      appearance: "none"
    }}>
              <Icon icon="code" size="12px" className="group-hover:bg-blue-500 dark:group-hover:bg-blue-300" />
              <span>{showCodeBlock ? "Hide code" : "Show code"}</span>
            </button> : null}

          <a className="inline-flex justify-end items-center hover:text-blue-500 dark:hover:text-blue-300 transition-colors group self-end gap-1" href={`${STACKBLITZ_BASE}/${example}?file=src/App.tsx`} target="_blank" rel="noreferrer">
            <Icon icon="bolt" size="12px" className="group-hover:bg-blue-500 dark:group-hover:bg-blue-300" />
            <span>StackBlitz demo</span>
          </a>
        </div>

        <div className="grid transition-[grid-template-rows] duration-300 ease-in-out overflow-auto overflow-y-hidden overflow-x-auto" style={showCodeBlock ? {
      gridTemplateRows: "1fr"
    } : {
      gridTemplateRows: "0fr"
    }}>
          <div style={{
      minHeight: 0,
      overflowX: "auto",
      overflowY: "hidden",
      marginBlockStart: "-1.25rem",
      marginBlockEnd: "-1.5rem"
    }}>
            {children}
          </div>
        </div>
      </Frame>;
  } else {
    return <div style={{
      display: "flex",
      width: fullWidth ? "100%" : "50%",
      minHeight: customHeight ? customHeight : "316px",
      resize: "vertical",
      overflow: "auto"
    }}>
        <iframe title={example} style={{
      flex: 1,
      width: fullWidth ? "100%" : "50%",
      minHeight: customHeight ? customHeight : "316px"
    }} src={`${STACKBLITZ_BASE}/${example}?embed=1&hideNavigation=1&hideExplorer=1&terminalHeight=0&file=src/App.tsx${clickToLoad ? "&ctl=1" : ""}${hideCodeInLiveCode ? "&view=preview" : ""}`} allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" />
      </div>;
  }
};

<Tabs>
  <Tab title="Implementation">
    <LiveCode showCode example="numberfield-playground" fullWidth screenshot>
      ```tsx lines expandable theme={null}
      import { NumberField } from "@servicetitan/anvil2";

      function App() {
        return (
          <NumberField
            label="Label"
            placeholder="Placeholder"
            hint="Hint text"
            moreInfo="More info"
            size="large"
            required
            loading
            error={false}
            style={{ maxWidth: "180px" }}
          />
        );
      }

      export default App;
      ```
    </LiveCode>

    ## Common Examples

    ```tsx theme={null}
    import { NumberField } from "@servicetitan/anvil2";

    function ExampleComponent() {
      return <NumberField label="Quantity" />;
    }
    ```

    ### Default values

    A default value can given to `NumberField` to use the field in an uncontrolled mode.

    <LiveCode showCode example="numberfield-defaultvalue" screenshot fullWidth>
      ```tsx lines expandable theme={null}
      import { NumberField } from "@servicetitan/anvil2";

      function App() {
        return <NumberField defaultValue={1337} />;
      }

      export default App;
      ```
    </LiveCode>

    ### Controlled usage

    The `NumberField` can be controlled. This means you can maintain the state outside the component. Listen for new values with `onChange` and supply state back into the `value` prop.

    <LiveCode showCode example="numberfield-controlled" screenshot fullWidth>
      ```tsx lines expandable theme={null}
      import { NumberField, Flex, Button } from "@servicetitan/anvil2";
      import { useState } from "react";

      function App() {
        const [value, setValue] = useState<number | null>(null);

        const handleChange = (value: number | null) => {
          setValue(value);
        };

        return (
          <Flex direction="column" gap="4">
            <NumberField
              label="Controlled Example"
              value={value}
              onChange={handleChange}
            />
            <Button
              onClick={() => {
                setValue(11);
              }}
            >
              Set to 11
            </Button>
          </Flex>
        );
      }

      export default App;
      ```
    </LiveCode>

    ### Empty

    The `NumberField` allows not only `0`, but also the ability for the input to be completely cleared out. This is represented by `null` in the `onChange`, `value`, and `defaultValue` props.

    <LiveCode showCode example="numberfield-null" screenshot fullWidth>
      ```tsx lines expandable theme={null}
      import { NumberField, Flex, Button } from "@servicetitan/anvil2";
      import { useState } from "react";

      function App() {
        const [value, setValue] = useState<number | null>(null);

        const handleChange = (value: number | null) => {
          setValue(value);
        };

        return (
          <Flex direction="column" gap="4">
            <NumberField label="Example" value={value} onChange={handleChange} />
            <Button
              onClick={() => {
                setValue(null);
              }}
            >
              Set to null
            </Button>
          </Flex>
        );
      }

      export default App;
      ```
    </LiveCode>

    ### Step value

    The increment and decrement functionality of the `NumberField` defaults to adding or subtracting `1`, but can be modified to add or subtract another number. This value applies to the visual +/- buttons as well as keyboard up/down keys.

    <LiveCode showCode example="numberfield-step" screenshot fullWidth>
      ```tsx lines expandable theme={null}
      import { NumberField } from "@servicetitan/anvil2";

      function App() {
        return <NumberField label="Step by 3" step={3} />;
      }

      export default App;
      ```
    </LiveCode>

    Note that `step` does not enforce that the value is a multiple of the step. Instead, it will add to or subtract from the current value.

    ### Min and max values

    The `minValue` and `maxValue` props define the bounds of the `NumberField`. The stepper buttons and keyboard up/down keys clamp the value between `minValue` and `maxValue`: if `value` + `step` > `maxValue`, the result of incrementing is `maxValue`, and if `value` - `step` \< `minValue`, the result is `minValue`.

    <LiveCode showCode example="numberfield-minvaluemaxvalue" screenshot fullWidth>
      ```tsx lines expandable theme={null}
      import { NumberField } from "@servicetitan/anvil2";

      function App() {
        return (
          <NumberField
            label="Step by 3"
            defaultValue={13}
            step={3}
            minValue={11}
            maxValue={22}
            description="Min 11, max 22"
          />
        );
      }

      export default App;
      ```
    </LiveCode>

    <Warning>`minValue` and `maxValue` do not strongly prevent the user from entering an out-of-bounds value by typing or pasting. The component does not validate input internally. When bounds must be enforced, handle validation in your own code and surface feedback via the `error` prop. See [Errors](#errors) for an example.</Warning>

    <LiveCode showCode example="numberfield-validation" screenshot fullWidth>
      ```tsx lines expandable theme={null}
      import { NumberField } from "@servicetitan/anvil2";

      function App() {
        return (
          <NumberField
            defaultValue={123456789}
            maxValue={100}
            description={"Max value: 100"}
          />
        );
      }

      export default App;
      ```
    </LiveCode>

    ### Negative values

    The `NumberField` defaults to a minimum of `0`, but supports negative number inputs by setting a lower `minValue`.

    <LiveCode showCode example="numberfield-negative" screenshot fullWidth>
      ```tsx lines expandable theme={null}
      import { NumberField } from "@servicetitan/anvil2";

      function App() {
        return <NumberField defaultValue={-100} minValue={-1000} />;
      }

      export default App;
      ```
    </LiveCode>

    ### Fractional values

    The `NumberField` defaults to integers, but supports decimal number inputs with the `maximumFractionDigits` and `minimumFractionDigits` props.

    <LiveCode showCode example="numberfield-minimumfractiondigits" screenshot fullWidth>
      ```tsx lines expandable theme={null}
      import { NumberField } from "@servicetitan/anvil2";

      function App() {
        return (
          <NumberField
            label="Decimal numbers"
            defaultValue={3.5}
            minimumFractionDigits={2}
          />
        );
      }

      export default App;
      ```
    </LiveCode>

    <Warning>Be careful! Forgetting to set `minimumFractionDigits` or `maximumFractionDigits` can result in your numbers being rounded unexpectedly.</Warning>

    <LiveCode showCode example="numberfield-rounding" screenshot fullWidth>
      ```tsx lines expandable theme={null}
      import { NumberField } from "@servicetitan/anvil2";

      function App() {
        return <NumberField defaultValue={3.5} description="Default value: 3.5" />;
      }

      export default App;
      ```
    </LiveCode>

    ### Hiding increment/decrement buttons

    <LiveCode showCode example="numberfield-hide-controls" screenshot fullWidth>
      ```tsx lines expandable theme={null}
      import { NumberField } from "@servicetitan/anvil2";

      function App() {
        return <NumberField label="Account Number" hideControls />;
      }

      export default App;
      ```
    </LiveCode>

    The +/- buttons can be optionally hidden using the `hideControls` prop. When hidden, users can still change the value by typing directly into the input or using keyboard up/down arrow keys.

    ### Errors

    Error messages can be applied to the component via the `error` prop.

    <LiveCode showCode example="numberfield-error" screenshot fullWidth>
      ```tsx lines expandable theme={null}
      import { NumberField } from "@servicetitan/anvil2";
      import { useState } from "react";

      function App() {
        const [value, setValue] = useState<number | null>(0);

        return (
          <NumberField
            label="Quantity"
            description="Type an even number"
            value={value}
            onChange={setValue}
            error={(value ?? 0) % 2 !== 0 ? "Must be an even amount" : ""}
          />
        );
      }

      export default App;
      ```
    </LiveCode>

    <Warning>The component does not do validation internally. For example, it may be possible for a user to enter a value outside the `minValue`/`maxValue` without knowing it unless you provide an error message. See [Min and max values](#min-and-max-values) for details.</Warning>

    ### Markdown in labels

    The `label` prop supports inline markdown: bold (`**text**`), italic (`*text*`), bold and italic (`***text***`), highlight (`==text==`), and code (`` `text` ``).

    <LiveCode showCode example="numberfield-markdownlabel" screenshot fullWidth>
      ```tsx lines expandable theme={null}
      import { Flex, NumberField } from "@servicetitan/anvil2";

      function App() {
        return (
          <Flex direction="column" gap="4" style={{ maxWidth: 400 }}>
            <NumberField label="**Bold** label" />
            <NumberField label="*Italic* label" />
            <NumberField label="***Bold and italic*** label" />
            <NumberField label="==Highlight== label" />
            <NumberField label="`Code` label" />
          </Flex>
        );
      }

      export default App;
      ```
    </LiveCode>

    ### Hide the label

    Use `hideLabel` to visually hide the label. The `label` string is converted to an `aria-label` on the input so it remains accessible to screen readers — any inline markdown is stripped to plain text.

    <LiveCode showCode example="numberfield-hidelabel" screenshot fullWidth>
      ```tsx lines expandable theme={null}
      import { Flex, NumberField } from "@servicetitan/anvil2";

      function App() {
        return (
          <Flex
            direction="row"
            gap="2"
            alignItems="flex-end"
            style={{ maxWidth: 400 }}
          >
            <NumberField label="Quantity" hideLabel suffix="qty" />
            <NumberField label="Unit price" hideLabel prefix="$" />
          </Flex>
        );
      }

      export default App;
      ```
    </LiveCode>
  </Tab>

  <Tab title="NumberField Props">
    ```tsx theme={null}
    <NumberField
      label="Quantity"
      minValue={0}
      maxValue={100}
      step={1}
      maximumFractionDigits={0}
      minimumFractionDigits={0}
      value={10}
      defaultValue={0}
      hideControls={false}
      onChange={(value) => console.log(value)}
      description="Enter a quantity"
      error="Invalid value"
      hint="Must be between 0 and 100"
      size="medium"
      loading={false}
      disabled={false}
      readOnly={false}
      required={false}
      placeholder="Enter quantity"
    />
    ```

    ## `NumberField` Props

    ### Properties

    <ParamField path="defaultValue" type={`number | null`}>
      Default value for uncontrolled number field.
    </ParamField>

    <ParamField path="hideControls" type="boolean" default="false">
      Whether to hide the increment/decrement buttons.
    </ParamField>

    <ParamField path="maximumFractionDigits" type="number" default="0">
      Maximum number of decimal places to display and allow.
    </ParamField>

    <ParamField path="maxValue" type="number">
      Maximum allowed value for the number field.
    </ParamField>

    <ParamField path="minimumFractionDigits" type="number" default="0">
      Minimum number of decimal places to display.
    </ParamField>

    <ParamField path="minValue" type="number">
      Minimum allowed value for the number field.
    </ParamField>

    <ParamField path="onChange" type="(value: number | null) => void">
      Callback fired when the value changes.
    </ParamField>

    <ParamField path="step" type="number" default="1">
      Step value for increment/decrement operations.
    </ParamField>

    <ParamField path="value" type={`number | null`}>
      Controlled value of the number field.
    </ParamField>

    ### Additional field props

    The component also support the following props which are common to most field components:

    <ParamField path="description" type="string">
      Descriptive text displayed below the input field
    </ParamField>

    <ParamField path="disabled" type="boolean" default="false">
      Whether the number field is disabled
    </ParamField>

    <ParamField path="errorAriaLive" type={`"off" | "polite" | "assertive"`} default="assertive">
      ARIA live region setting for error announcements
    </ParamField>

    <ParamField path="error" type="ReactElement | string | boolean">
      Error message or element to display below the input
    </ParamField>

    <ParamField path="hideLabel" type="boolean" default="false">
      Visually hides the label while keeping it accessible to screen readers via `aria-label`.
    </ParamField>

    <ParamField path="hint" type="string">
      Hint text displayed below the input field
    </ParamField>

    <ParamField path="label" type="string">
      Label for the field. Supports inline markdown formatting.

      <Warning>Omitting `label` is deprecated and will be required in v4.0.0. Use `hideLabel` to visually hide it.</Warning>
    </ParamField>

    <ParamField path="labelProps" type="object">
      Additional props to pass to the `Label` component
    </ParamField>

    <ParamField path="loading" type="boolean" default="false">
      Whether to show a loading spinner in the input field
    </ParamField>

    <ParamField path="moreInfo" type="string">
      Additional information to display with the label
    </ParamField>

    <ParamField path="placeholder" type="string">
      Placeholder text when no value is present
    </ParamField>

    <ParamField path="prefix" type="string | ReactElement" />

    <ParamField path="readOnly" type="boolean" default="false">
      Whether the number field is read-only
    </ParamField>

    <ParamField path="required" type="boolean" default="false">
      Whether the number field is required
    </ParamField>

    <ParamField path="size" type={`"small" | "medium" | "large"`} default="medium">
      Size variant of the number field
    </ParamField>

    <ParamField path="suffix" type="string | ReactElement" />
  </Tab>
</Tabs>
