packages/codemod/docs/NUMBER_INPUT_MIGRATION.md
This document outlines the migration from Chakra UI v2 NumberInput component to v3.
import {
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
} from "@chakra-ui/react"
;<NumberInput>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
import { NumberInput } from "@chakra-ui/react"
;<NumberInput.Root>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.IncrementTrigger />
<NumberInput.DecrementTrigger />
</NumberInput.Control>
</NumberInput.Root>
| v2 Prop | v3 Prop | Notes |
|---|---|---|
isDisabled | disabled | Standard HTML attribute |
isInvalid | invalid | Simplified prop name |
isReadOnly | readOnly | Standard prop name |
isRequired | required | Standard HTML attribute |
| v2 Prop | v3 Prop | Notes |
|---|---|---|
value | value | Now accepts string instead of number |
defaultValue | defaultValue | Now accepts string instead of number |
| v2 Prop | v3 Prop | Notes |
|---|---|---|
onChange | onValueChange | Receives object: { value, valueAsNumber } |
onInvalid | onValueInvalid | Called when input is invalid |
| v2 Prop | v3 Prop | Notes |
|---|---|---|
keepWithinRange | allowOverflow | Inverse boolean: false → true, true → false |
| v2 Prop | v3 Equivalent | Notes |
|---|---|---|
isValidCharacter | (removed) | No longer supported in v3 |
| v2 Prop | v3 Prop/Behavior | Notes |
|---|---|---|
focusBorderColor | CSS variable: --focus-color | Moved to css prop object |
errorBorderColor | CSS variable: --error-color | Moved to css prop object |
v2:
import {
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
} from "@chakra-ui/react"
;<NumberInput>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
v3:
import { NumberInput } from "@chakra-ui/react"
;<NumberInput.Root>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.IncrementTrigger />
<NumberInput.DecrementTrigger />
</NumberInput.Control>
</NumberInput.Root>
v2:
import {
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
} from "@chakra-ui/react"
;<NumberInput defaultValue={15} min={10} max={20}>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
v3:
import { NumberInput } from "@chakra-ui/react"
;<NumberInput.Root defaultValue="15" min={10} max={20}>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.IncrementTrigger />
<NumberInput.DecrementTrigger />
</NumberInput.Control>
</NumberInput.Root>
v2:
import {
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
} from "@chakra-ui/react"
;<NumberInput step={5} defaultValue={15} min={10} max={30}>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
v3:
import { NumberInput } from "@chakra-ui/react"
;<NumberInput.Root step={5} defaultValue="15" min={10} max={30}>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.IncrementTrigger />
<NumberInput.DecrementTrigger />
</NumberInput.Control>
</NumberInput.Root>
v2:
import {
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
} from "@chakra-ui/react"
;<NumberInput defaultValue={15} precision={2} step={0.2}>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
v3:
import { NumberInput } from "@chakra-ui/react"
;<NumberInput.Root defaultValue="15" precision={2} step={0.2}>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.IncrementTrigger />
<NumberInput.DecrementTrigger />
</NumberInput.Control>
</NumberInput.Root>
v2:
import { NumberInput, NumberInputField } from "@chakra-ui/react"
;<NumberInput isDisabled>
<NumberInputField />
</NumberInput>
v3:
import { NumberInput } from "@chakra-ui/react"
;<NumberInput.Root disabled>
<NumberInput.Input />
</NumberInput.Root>
v2:
import { NumberInput, NumberInputField } from "@chakra-ui/react"
;<NumberInput isInvalid>
<NumberInputField />
</NumberInput>
v3:
import { NumberInput } from "@chakra-ui/react"
;<NumberInput.Root invalid>
<NumberInput.Input />
</NumberInput.Root>
v2:
import { NumberInput, NumberInputField } from "@chakra-ui/react"
;<NumberInput isReadOnly>
<NumberInputField />
</NumberInput>
v3:
import { NumberInput } from "@chakra-ui/react"
;<NumberInput.Root readOnly>
<NumberInput.Input />
</NumberInput.Root>
v2:
import { NumberInput, NumberInputField } from "@chakra-ui/react"
;<NumberInput isRequired>
<NumberInputField />
</NumberInput>
v3:
import { NumberInput } from "@chakra-ui/react"
;<NumberInput.Root required>
<NumberInput.Input />
</NumberInput.Root>
v2:
import {
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
} from "@chakra-ui/react"
import React from "react"
function Example() {
const format = (val) => "$" + val
const parse = (val) => val.replace(/^\$/, "")
const [value, setValue] = React.useState("1.53")
return (
<NumberInput
onChange={(valueString) => setValue(parse(valueString))}
value={format(value)}
max={50}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
)
}
v3:
import { NumberInput } from "@chakra-ui/react"
import React from "react"
function Example() {
const format = (val) => "$" + val
const parse = (val) => val.replace(/^\$/, "")
const [value, setValue] = React.useState("1.53")
return (
<NumberInput.Root
onValueChange={(e) => setValue(parse(e.value))}
value={format(value)}
max={50}
>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.IncrementTrigger />
<NumberInput.DecrementTrigger />
</NumberInput.Control>
</NumberInput.Root>
)
}
v2:
import {
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
Stack,
} from "@chakra-ui/react"
;<Stack shouldWrapChildren direction="row">
<NumberInput size="xs" maxW={16} defaultValue={15} min={10}>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
<NumberInput size="sm" maxW={20} defaultValue={15} min={10}>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
<NumberInput size="md" maxW={24} defaultValue={15} min={10}>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
<NumberInput size="lg" maxW={32} defaultValue={15} min={10}>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</Stack>
v3:
import { NumberInput, Stack } from "@chakra-ui/react"
;<Stack shouldWrapChildren direction="row">
<NumberInput.Root size="xs" maxW={16} defaultValue="15" min={10}>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.IncrementTrigger />
<NumberInput.DecrementTrigger />
</NumberInput.Control>
</NumberInput.Root>
<NumberInput.Root size="sm" maxW={20} defaultValue="15" min={10}>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.IncrementTrigger />
<NumberInput.DecrementTrigger />
</NumberInput.Control>
</NumberInput.Root>
<NumberInput.Root size="md" maxW={24} defaultValue="15" min={10}>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.IncrementTrigger />
<NumberInput.DecrementTrigger />
</NumberInput.Control>
</NumberInput.Root>
<NumberInput.Root size="lg" maxW={32} defaultValue="15" min={10}>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.IncrementTrigger />
<NumberInput.DecrementTrigger />
</NumberInput.Control>
</NumberInput.Root>
</Stack>
v2:
import {
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
} from "@chakra-ui/react"
;<NumberInput size="sm" defaultValue={15} min={10}>
<NumberInputField focusBorderColor="red.200" />
<NumberInputStepper>
<NumberIncrementStepper
bg="green.200"
_active={{ bg: "green.300" }}
children="+"
/>
<NumberDecrementStepper
bg="pink.200"
_active={{ bg: "pink.300" }}
children="-"
/>
</NumberInputStepper>
</NumberInput>
v3:
import { NumberInput } from "@chakra-ui/react"
;<NumberInput.Root size="sm" defaultValue="15" min={10}>
<NumberInput.Input css={{ "--focus-color": "red.200" }} />
<NumberInput.Control>
<NumberInput.IncrementTrigger bg="green.200" _active={{ bg: "green.300" }}>
+
</NumberInput.IncrementTrigger>
<NumberInput.DecrementTrigger bg="pink.200" _active={{ bg: "pink.300" }}>
-
</NumberInput.DecrementTrigger>
</NumberInput.Control>
</NumberInput.Root>
v2:
import {
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
} from "@chakra-ui/react"
function MouseWheelExample() {
return (
<NumberInput allowMouseWheel>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
)
}
v3:
import { NumberInput } from "@chakra-ui/react"
function MouseWheelExample() {
return (
<NumberInput.Root allowMouseWheel>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.IncrementTrigger />
<NumberInput.DecrementTrigger />
</NumberInput.Control>
</NumberInput.Root>
)
}
v2:
import {
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
} from "@chakra-ui/react"
;<NumberInput defaultValue={15} max={30} clampValueOnBlur={false}>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
v3:
import { NumberInput } from "@chakra-ui/react"
;<NumberInput.Root defaultValue="15" max={30} clampValueOnBlur={false}>
<NumberInput.Input />
<NumberInput.Control>
<NumberInput.IncrementTrigger />
<NumberInput.DecrementTrigger />
</NumberInput.Control>
</NumberInput.Root>
To automatically migrate your NumberInput components, run:
npx @chakra-ui/codemod transform number-input <path>
--dry - Do a dry-run without making changes--print - Print the changed output for comparison# Transform all files in src directory
npx @chakra-ui/codemod transform number-input ./src
# Dry run to preview changes
npx @chakra-ui/codemod transform number-input ./src --dry
# Print changes for comparison
npx @chakra-ui/codemod transform number-input ./src --print
If you prefer to migrate manually:
Update Component Names:
<NumberInput> → <NumberInput.Root><NumberInputField> → <NumberInput.Input><NumberInputStepper> → <NumberInput.Control><NumberIncrementStepper> → <NumberInput.IncrementTrigger><NumberDecrementStepper> → <NumberInput.DecrementTrigger>Update Props:
isDisabled → disabledisInvalid → invalidisReadOnly → readOnlyisRequired → requiredonChange → onValueChange (receives { value, valueAsNumber })onInvalid → onValueInvalidkeepWithinRange → allowOverflow (inverse: false → true, true →
false)Remove Unsupported Props:
isValidCharacterUpdate Styling Props:
focusBorderColor='red.200' → css={{ '--focus-color': 'red.200' }}errorBorderColor='red.500' → css={{ '--error-color': 'red.500' }}NumberInput → NumberInput.Root (namespace component)NumberInputField → NumberInput.InputNumberInputStepper → NumberInput.ControlNumberIncrementStepper → NumberInput.IncrementTriggerNumberDecrementStepper → NumberInput.DecrementTriggerisDisabled → disabledisInvalid → invalidisReadOnly → readOnlyisRequired → requiredonChange → onValueChangeonInvalid → onValueInvalidvalue now accepts string instead of numberdefaultValue now accepts string instead of numberkeepWithinRange → allowOverflow (inverse boolean)
keepWithinRange={false} becomes allowOverflow={true}keepWithinRange={true} becomes allowOverflow={false}isValidCharacter - no longer supportedonChange(value) → onValueChange({ value, valueAsNumber })onInvalid(message, value) → onValueInvalid({ reason, value })focusBorderColor and errorBorderColor moved to CSS variablescss prop: css={{ '--focus-color': 'red.200' }}The codemod will:
isDisabled → disabledisInvalid → invalidisReadOnly → readOnlyisRequired → requiredonChange, onInvalidkeepWithinRange to allowOverflow (inverse boolean)isValidCharactervalue and defaultValue to stringsfocusBorderColor and errorBorderColor to CSS variablesThe v3 NumberInput component provides:
value and defaultValue now accept strings instead of
numbers in v3:
value={15} → value="15"value={myVar} → value={String(myVar)}value={format(val)} →
value={String(format(val))}value="15" → value="15"onValueChange handler in v3 receives an object
{ value, valueAsNumber }, not just the value string
value is the string representationvalueAsNumber is the parsed number valuefocusBorderColor and errorBorderColor props are moved to CSS variables
in the css propkeepWithinRange from v2 is converted to allowOverflow in v3 with inverse
boolean logicisValidCharacter is no longer supported in v3IncrementTrigger and
DecrementTrigger componentsallowMouseWheel prop