Number Input
A field that allows user input of numeric values.
import { NumberInput, type NumberInputProps } from '~/components/ui/number-input'
export const Demo = (props: NumberInputProps) => {
  return (
    <NumberInput defaultValue="3" {...props}>
      Label
    </NumberInput>
  )
}
import { NumberInput, type NumberInputProps } from '~/components/ui/number-input'
export const Demo = (props: NumberInputProps) => {
  return (
    <NumberInput defaultValue="3" {...props}>
      Label
    </NumberInput>
  )
}
Usage
import { NumberInput } from '~/components/ui/number-input'Installation
npx @park-ui/cli components add number-input1
Add Styled Primitive
Copy the code snippet below into ~/components/ui/styled/number-input.tsx
'use client'
import type { Assign } from '@ark-ui/react'
import { NumberInput } from '@ark-ui/react/number-input'
import { type NumberInputVariantProps, numberInput } from 'styled-system/recipes'
import type { ComponentProps, HTMLStyledProps } from 'styled-system/types'
import { createStyleContext } from './utils/create-style-context'
const { withProvider, withContext } = createStyleContext(numberInput)
export type RootProviderProps = ComponentProps<typeof RootProvider>
export const RootProvider = withProvider<
  HTMLDivElement,
  Assign<Assign<HTMLStyledProps<'div'>, NumberInput.RootProviderBaseProps>, NumberInputVariantProps>
>(NumberInput.RootProvider, 'root')
export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
  HTMLDivElement,
  Assign<Assign<HTMLStyledProps<'div'>, NumberInput.RootBaseProps>, NumberInputVariantProps>
>(NumberInput.Root, 'root')
export const Control = withContext<
  HTMLDivElement,
  Assign<HTMLStyledProps<'div'>, NumberInput.ControlBaseProps>
>(NumberInput.Control, 'control')
export const DecrementTrigger = withContext<
  HTMLButtonElement,
  Assign<HTMLStyledProps<'button'>, NumberInput.DecrementTriggerBaseProps>
>(NumberInput.DecrementTrigger, 'decrementTrigger')
export const IncrementTrigger = withContext<
  HTMLButtonElement,
  Assign<HTMLStyledProps<'button'>, NumberInput.IncrementTriggerBaseProps>
>(NumberInput.IncrementTrigger, 'incrementTrigger')
export const Input = withContext<
  HTMLInputElement,
  Assign<HTMLStyledProps<'input'>, NumberInput.InputBaseProps>
>(NumberInput.Input, 'input')
export const Label = withContext<
  HTMLLabelElement,
  Assign<HTMLStyledProps<'label'>, NumberInput.LabelBaseProps>
>(NumberInput.Label, 'label')
export const Scrubber = withContext<
  HTMLDivElement,
  Assign<HTMLStyledProps<'div'>, NumberInput.ScrubberBaseProps>
>(NumberInput.Scrubber, 'scrubber')
export const ValueText = withContext<
  HTMLSpanElement,
  Assign<HTMLStyledProps<'span'>, NumberInput.ValueTextBaseProps>
>(NumberInput.ValueText, 'valueText')
export { NumberInputContext as Context } from '@ark-ui/react/number-input'
import { type Assign, NumberInput } from '@ark-ui/solid'
import type { ComponentProps } from 'solid-js'
import { type NumberInputVariantProps, numberInput } from 'styled-system/recipes'
import type { HTMLStyledProps } from 'styled-system/types'
import { createStyleContext } from './utils/create-style-context'
const { withProvider, withContext } = createStyleContext(numberInput)
export type RootProviderProps = ComponentProps<typeof RootProvider>
export const RootProvider = withProvider<
  Assign<Assign<HTMLStyledProps<'div'>, NumberInput.RootProviderBaseProps>, NumberInputVariantProps>
>(NumberInput.RootProvider, 'root')
export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
  Assign<Assign<HTMLStyledProps<'div'>, NumberInput.RootBaseProps>, NumberInputVariantProps>
>(NumberInput.Root, 'root')
export const Control = withContext<Assign<HTMLStyledProps<'div'>, NumberInput.ControlBaseProps>>(
  NumberInput.Control,
  'control',
)
export const DecrementTrigger = withContext<
  Assign<HTMLStyledProps<'button'>, NumberInput.DecrementTriggerBaseProps>
>(NumberInput.DecrementTrigger, 'decrementTrigger')
export const IncrementTrigger = withContext<
  Assign<HTMLStyledProps<'button'>, NumberInput.IncrementTriggerBaseProps>
>(NumberInput.IncrementTrigger, 'incrementTrigger')
export const Input = withContext<Assign<HTMLStyledProps<'input'>, NumberInput.InputBaseProps>>(
  NumberInput.Input,
  'input',
)
export const Label = withContext<Assign<HTMLStyledProps<'label'>, NumberInput.LabelBaseProps>>(
  NumberInput.Label,
  'label',
)
export const Scrubber = withContext<Assign<HTMLStyledProps<'div'>, NumberInput.ScrubberBaseProps>>(
  NumberInput.Scrubber,
  'scrubber',
)
export const ValueText = withContext<Assign<HTMLStyledProps<'span'>, NumberInput.ValueTextProps>>(
  NumberInput.ValueText,
  'valueText',
)
export { NumberInputContext as Context } from '@ark-ui/solid'
No snippet found2
Add Composition
Copy the composition snippet below into ~/components/ui/number-input.tsx
import { forwardRef } from 'react'
import * as StyledNumberInput from './styled/number-input'
export interface NumberInputProps extends StyledNumberInput.RootProps {}
export const NumberInput = forwardRef<HTMLDivElement, NumberInputProps>((props, ref) => {
  const { children, ...rootProps } = props
  return (
    <StyledNumberInput.Root ref={ref} {...rootProps}>
      {children && <StyledNumberInput.Label>{children}</StyledNumberInput.Label>}
      <StyledNumberInput.Control>
        <StyledNumberInput.Input />
        <StyledNumberInput.IncrementTrigger>
          <ChevronUpIcon />
        </StyledNumberInput.IncrementTrigger>
        <StyledNumberInput.DecrementTrigger>
          <ChevronDownIcon />
        </StyledNumberInput.DecrementTrigger>
      </StyledNumberInput.Control>
    </StyledNumberInput.Root>
  )
})
NumberInput.displayName = 'NumberInput'
const ChevronUpIcon = () => (
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
    <title>Chevron Up Icon</title>
    <path
      fill="none"
      stroke="currentColor"
      strokeLinecap="round"
      strokeLinejoin="round"
      strokeWidth="2"
      d="m18 15l-6-6l-6 6"
    />
  </svg>
)
const ChevronDownIcon = () => (
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
    <title>Chevron Down Icon</title>
    <path
      fill="none"
      stroke="currentColor"
      strokeLinecap="round"
      strokeLinejoin="round"
      strokeWidth="2"
      d="m6 9l6 6l6-6"
    />
  </svg>
)
import { Show, children } from 'solid-js'
import * as StyledNumberInput from './styled/number-input'
export interface NumberInputProps extends StyledNumberInput.RootProps {}
export const NumberInput = (props: NumberInputProps) => {
  const getChildren = children(() => props.children)
  return (
    <StyledNumberInput.Root {...props}>
      <Show when={getChildren()}>
        <StyledNumberInput.Label>{getChildren()}</StyledNumberInput.Label>
      </Show>
      <StyledNumberInput.Control>
        <StyledNumberInput.Input />
        <StyledNumberInput.IncrementTrigger>
          <ChevronUpIcon />
        </StyledNumberInput.IncrementTrigger>
        <StyledNumberInput.DecrementTrigger>
          <ChevronDownIcon />
        </StyledNumberInput.DecrementTrigger>
      </StyledNumberInput.Control>
    </StyledNumberInput.Root>
  )
}
const ChevronUpIcon = () => (
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
    <title>Chevron Up Icon</title>
    <path
      fill="none"
      stroke="currentColor"
      stroke-linecap="round"
      stroke-linejoin="round"
      stroke-width="2"
      d="m18 15l-6-6l-6 6"
    />
  </svg>
)
const ChevronDownIcon = () => (
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
    <title>Chevron Down Icon</title>
    <path
      fill="none"
      stroke="currentColor"
      stroke-linecap="round"
      stroke-linejoin="round"
      stroke-width="2"
      d="m6 9l6 6l6-6"
    />
  </svg>
)
3
Integrate Recipe
If you're not using @park-ui/preset, add the following recipe to yourpanda.config.ts:
import { numberInputAnatomy } from '@ark-ui/anatomy'
import { defineSlotRecipe } from '@pandacss/dev'
const trigger = {
  alignItems: 'center',
  borderColor: 'border.default',
  color: 'fg.muted',
  cursor: 'pointer',
  display: 'inline-flex',
  justifyContent: 'center',
  transitionDuration: 'normal',
  transitionProperty: 'background, border-color, color, box-shadow',
  transitionTimingFunction: 'default',
  '& :where(svg)': {
    width: '4',
    height: '4',
  },
  _hover: {
    background: 'gray.a2',
    color: 'fg.default',
  },
  _disabled: {
    color: 'fg.disabled',
    cursor: 'not-allowed',
    _hover: {
      background: 'transparent',
      color: 'fg.disabled',
    },
  },
}
export const numberInput = defineSlotRecipe({
  className: 'numberInput',
  slots: numberInputAnatomy.keys(),
  base: {
    root: {
      display: 'flex',
      flexDirection: 'column',
      gap: '1.5',
    },
    control: {
      borderColor: 'border.default',
      borderRadius: 'l2',
      borderWidth: '1px',
      display: 'grid',
      divideX: '1px',
      gridTemplateColumns: '1fr 32px',
      gridTemplateRows: '1fr 1fr',
      overflow: 'hidden',
      transitionDuration: 'normal',
      transitionProperty: 'border-color, box-shadow',
      transitionTimingFunction: 'default',
      _focusWithin: {
        borderColor: 'colorPalette.default',
        boxShadow: '0 0 0 1px var(--colors-color-palette-default)',
      },
      _disabled: {
        opacity: 0.4,
        cursor: 'not-allowed',
      },
    },
    input: {
      background: 'transparent',
      border: 'none',
      gridRow: '2',
      outline: 'none',
      width: 'full',
      _disabled: {
        cursor: 'not-allowed',
      },
    },
    label: {
      color: 'fg.default',
      fontWeight: 'medium',
    },
    decrementTrigger: { ...trigger, borderTopWidth: '1px' },
    incrementTrigger: trigger,
  },
  defaultVariants: {
    size: 'md',
  },
  variants: {
    size: {
      md: {
        control: {
          ps: '3',
          h: '10',
          minW: '10',
          fontSize: 'md',
        },
        label: {
          textStyle: 'sm',
        },
      },
      lg: {
        control: {
          ps: '3.5',
          h: '11',
          minW: '11',
          fontSize: 'md',
        },
        label: {
          textStyle: 'sm',
        },
      },
      xl: {
        control: {
          ps: '4',
          h: '12',
          minW: '12',
          fontSize: 'lg',
        },
        label: {
          textStyle: 'md',
        },
      },
    },
  },
})