NumberInput
NumberInput
사용자로부터 숫자를 입력받는 컴포넌트예요. react-number-format을 기반으로 해요.
출처: 문서
본문
사용법 (Usage)
NumberInput은 react-number-format을 기반으로 해요. 원본 패키지의 NumericFormat 컴포넌트 prop 대부분을 지원해요.
NumberInput 컴포넌트는 Input 및 Input.Wrapper 컴포넌트 기능과 모든 input 요소 prop을 지원해요. NumberInput 문서에는 컴포넌트가 지원하는 모든 기능이 포함되지는 않아요. 사용 가능한 모든 기능은 Input 문서를 참고해요.
import { NumberInput } from '@mantine/core';
function Demo() {
return <NumberInput label="Quantity" placeholder="Enter quantity" />;
}
로딩 상태 (Loading state)
loading prop을 설정하면 로딩 인디케이터가 표시돼요. 기본적으로 로더는 인풋 오른쪽에 표시돼요. loadingPosition prop을 'left' 또는 'right'로 바꿔 위치를 변경할 수 있어요. API 호출, 검색, 검증 같은 비동기 작업에 유용해요.
import { NumberInput } from '@mantine/core';
function Demo() {
return <NumberInput label="Quantity" loading />;
}
제어 방식 (Controlled)
import { useState } from 'react';
import { NumberInput } from '@mantine/core';
function Demo() {
const [value, setValue] = useState<string | number>('');
return <NumberInput value={value} onChange={setValue} />;
}
비제어 방식 (Uncontrolled)
NumberInput은 네이티브 input[type="number"]와 같은 방식으로 비제어 폼에서 사용할 수 있어요. 폼 제출 시 FormData 객체에 숫자 인풋 값을 포함하려면 name 속성을 설정해요. 비제어 폼에서 초기 값을 제어하려면 defaultValue prop을 사용해요.
FormData와 함께 비제어 NumberInput을 사용하는 예시:
import { NumberInput } from '@mantine/core';
function Demo() {
return (
<form
onSubmit={(event) => {
event.preventDefault();
const formData = new FormData(event.currentTarget);
console.log('Number input value:', formData.get('quantity'));
}}
>
<NumberInput name="quantity" />
<button type="submit">Submit</button>
</form>
);
}
값 타입 (Value type)
value, defaultValue, onChange prop은 문자열 또는 숫자일 수 있어요. NumberInput 값을 숫자로 표현할 수 있는 모든 경우에 onChange 함수는 숫자로 호출돼요(예: 55, 1.28, -100). 하지만 값을 숫자로 표현할 수 없는 경우가 몇 가지 있어요.
- 빈 상태는 빈 문자열
''로 표현돼요 - 단독 마이너스 부호는 문자열
'-'로 표현돼요 Number.MAX_SAFE_INTEGER - 1보다 크거나Number.MIN_SAFE_INTEGER + 1보다 작은 숫자는 문자열로 표현돼요 –'90071992547409910'- 끝에 소수 구분 기호나 소수 0이 있는 숫자는 문자열로 표현돼요 –
'0.','0.0','-0.00'등
BigInt 값
NumberInput은 bigint 값도 지원해요. BigInt 모드는 value 또는 defaultValue에서 추론돼요.
value/defaultValue는bigint | stringonChange는bigint | string을 받음min,max,step,startValue는bigint지원- BigInt 모드는 정수 전용(
allowDecimal/소수 포맷팅 prop은 소수 파싱을 활성화하지 않음)
string은 중간 상태('' 또는 '-' 같은)의 폴백으로 여전히 사용돼요.
import { useState } from 'react';
import { NumberInput } from '@mantine/core';
function Demo() {
const [value, setValue] = useState(BigInt('12345678901234567890'));
return <NumberInput value={value} onChange={setValue} />;
}
onChange vs onValueChange
NumberInput은 값 변경 처리를 위한 두 개의 콜백 prop을 제공해요.
onChange: 단순화된 값(number | string, BigInt 모드에서는bigint | string)을 받아요. 대부분의 사용 사례에서 권장되는 콜백이에요. 값은 가능하면 숫자/bigint이고, 엣지 케이스(빈 인풋, 매우 큰 숫자, 끝 소수, 중간 BigInt 입력 상태)에서는 문자열이에요.onValueChange:react-number-format의 전체 payload를 받아요. 여기에는 다음이 포함돼요:floatValue: 숫자 값 (또는undefined)formattedValue: 형식화된 문자열 값 (prefix/suffix/구분 기호 포함)value: 원시 비형식 문자열 값- 변경 소스에 대한 추가 메타데이터
형식화된 값이나 변경에 대한 메타데이터(예: 사용자 타이핑, 증감 버튼, 프로그램 방식 변경에서 온 것인지)에 접근해야 한다면 onValueChange를 사용해요. 간단한 폼 처리는 onChange로 충분해요.
import { NumberInput } from '@mantine/core';
function Demo() {
return (
<NumberInput
prefix="$"
onChange={(value) => console.log('Simple value:', value)}
// onValueChange receives: { floatValue: 1234, formattedValue: '$1,234', value: '1234' }
onValueChange={(payload) => console.log('Full payload:', payload)}
/>
);
}
min과 max
min과 max prop으로 인풋 값을 제한할 수 있어요.
import { NumberInput } from '@mantine/core';
function Demo() {
return <NumberInput min={10} max={20} label="Enter value between 10 and 20" />;
}
클램프 동작 (Clamp behavior)
기본적으로 값은 인풋이 블러될 때 클램프돼요. clampBehavior="strict"로 설정하면 min/max 범위 밖의 값을 입력할 수 없어요. 이 옵션은 촘촘한 min과 max(예: min={10}과 max={20})가 있으면 문제가 생길 수 있어요. 값 클램핑을 완전히 비활성화해야 한다면 clampBehavior="none"을 설정해요.
import { NumberInput } from '@mantine/core';
function Demo() {
return <NumberInput min={0} max={100} clampBehavior="strict" defaultValue={50} />;
}
경계 콜백 (Boundary callbacks)
onMinReached와 onMaxReached를 사용해 값이 min 또는 max 경계에 도달할 때 함수를 호출할 수 있어요. 이 콜백은 사용자가 컨트롤이나 키보드 화살표로 max 이상 증가하거나 min 이하로 감소하려 할 때 트리거돼요.
import { NumberInput } from '@mantine/core';
function Demo() {
return (
<NumberInput
min={0}
max={100}
onMinReached={() => console.log('Minimum value reached')}
onMaxReached={() => console.log('Maximum value reached')}
/>
);
}
포커스 시 모두 선택 (Select all on focus)
selectAllOnFocus로 설정하면 필드가 포커스를 받을 때 전체 인풋 값을 자동으로 선택해요. 사용자가 값을 편집하기보다 교체할 것으로 예상될 때 유용해요.
import { NumberInput } from '@mantine/core';
function Demo() {
return <NumberInput selectAllOnFocus defaultValue="100" />;
}
접두사와 접미사 (Prefix and suffix)
prefix와 suffix prop으로 인풋 값의 시작이나 끝에 주어진 문자열을 추가할 수 있어요.
import { NumberInput } from '@mantine/core';
function Demo() {
return (
<>
<NumberInput prefix="$ " label="With prefix" />
<NumberInput suffix=" RUB" label="With suffix" mt="md" />
</>
);
}
음수 (Negative numbers)
기본적으로 음수는 허용돼요. allowNegative={false}로 설정하면 양수만 허용할 수 있어요.
import { NumberInput } from '@mantine/core';
function Demo() {
return <NumberInput allowNegative={false} label="Negative number are not allowed" />;
}
소수 (Decimal numbers)
기본적으로 소수는 허용돼요. allowDecimal={false}로 설정하면 정수만 허용할 수 있어요.
import { NumberInput } from '@mantine/core';
function Demo() {
return <NumberInput allowDecimal={false} label="Decimals are not allowed" />;
}
소수 자릿수 (Decimal scale)
decimalScale은 허용되는 소수 자릿수를 제어해요.
import { NumberInput } from '@mantine/core';
function Demo() {
return <NumberInput decimalScale={2} label="You can enter only 2 digits after decimal point" />;
}
고정 소수 자릿수 (Fixed decimal scale)
fixedDecimalScale로 설정하면 항상 고정된 소수 자릿수를 표시해요.
import { NumberInput } from '@mantine/core';
function Demo() {
return <NumberInput fixedDecimalScale decimalScale={2} defaultValue={4.5} label="Always show 2 digits after decimal point" />;
}
소수 구분 기호 (Decimal separator)
decimalSeparator로 소수 구분 기호 문자를 변경할 수 있어요.
import { NumberInput } from '@mantine/core';
function Demo() {
return <NumberInput decimalSeparator="," decimalScale={2} label="Custom decimal separator" />;
}
천 단위 구분 기호 (Thousand separator)
thousandSeparator prop으로 천 단위를 문자로 구분할 수 있어요. thousandsGroupStyle으로 그룹핑 로직을 제어할 수 있는데, thousand, lakh, wan, none 값을 받아요.
import { NumberInput } from '@mantine/core';
function Demo() {
return (
<>
<NumberInput thousandSeparator label="Thousands are separated with a comma" />
<NumberInput thousandSeparator=" " label="Thousands are separated with a space" mt="md" />
</>
);
}
블러 시 선행 0 제거 (Trim leading zeros on blur)
기본적으로 인풋이 포커스를 잃으면 선행 0이 제거돼요(예: 00100은 100이 됨). trimLeadingZeroesOnBlur={false}로 설정하면 이 동작을 비활성화할 수 있어요.
import { NumberInput } from '@mantine/core';
function Demo() {
return (
<>
<NumberInput trimLeadingZeroesOnBlur label="Leading zeros removed on blur" />
<NumberInput trimLeadingZeroesOnBlur={false} label="Leading zeros preserved" mt="md" />
</>
);
}
왼쪽·오른쪽 섹션 (Left and right sections)
NumberInput은 leftSection과 rightSection prop을 지원해요. 이 섹션들은 인풋 래퍼 안에서 절대 위치로 렌더링돼요. 아이콘, 인풋 컨트롤 또는 다른 요소를 표시하는 데 사용할 수 있어요.
섹션 스타일과 콘텐츠를 제어하려면 다음 prop을 사용할 수 있어요.
rightSection/leftSection– 인풋의 해당 쪽에 렌더링할 React 노드rightSectionWidth/leftSectionWidth– 오른쪽 섹션의 너비와 인풋 해당 쪽의 패딩을 제어해요. 기본적으로 컴포넌트sizeprop에 의해 제어돼요.rightSectionPointerEvents/leftSectionPointerEvents– 섹션의pointer-events속성을 제어해요. 비대화형 요소를 렌더링하고 싶다면none으로 설정해 클릭이 인풋으로 통과하게 해요.
import { NumberInput } from '@mantine/core';
import { CurrencyEthIcon } from '@phosphor-icons/react';
function Demo() {
const icon = <CurrencyEthIcon size={16} />;
return (
<>
<NumberInput leftSection={icon} label="With left section" />
<NumberInput rightSection={icon} label="With right section" mt="md" />
</>
);
}
증감 컨트롤 (Increment/decrement controls)
기본적으로 오른쪽 섹션은 증가·감소 버튼이 차지해요. 숨기려면 hideControls prop을 설정해요. 오른쪽 섹션에 기본 컨트롤을 대체할 무엇이든 렌더링하려면 rightSection prop을 사용할 수도 있어요.
import { NumberInput } from '@mantine/core';
import { ChartScatterIcon } from '@phosphor-icons/react';
function Demo() {
return (
<>
<NumberInput hideControls label="Hide controls" />
<NumberInput
rightSection={<ChartScatterIcon size={16} />}
rightSectionPointerEvents="none"
label="Custom right section"
mt="md"
/>
</>
);
}
홀드 시 증감 (Increment/decrement on hold)
stepHoldDelay와 stepHoldInterval prop으로 증감 컨트롤을 클릭하고 누르고 있을 때의 동작을 정의할 수 있어요.
import { NumberInput } from '@mantine/core';
function Demo() {
return (
<>
<NumberInput stepHoldDelay={500} stepHoldInterval={100} label="Step on hold" defaultValue={100} />
<NumberInput
stepHoldDelay={500}
stepHoldInterval={(t) => Math.max(1000 / t ** 2, 25)}
label="Step the value with interval function"
defaultValue={100}
/>
</>
);
}
커스텀 증감 컨트롤 (Custom increment and decrement controls)
increment와 decrement 함수가 있는 ref를 얻어 커스텀 컨트롤을 만들 수 있어요.
import { useRef } from 'react';
import { NumberInput, Group, Button, NumberInputHandlers } from '@mantine/core';
function Demo() {
const handlersRef = useRef<NumberInputHandlers>(null);
return (
<>
<NumberInput
value={100}
step={2}
handlersRef={handlersRef}
label="Click buttons to change value"
/>
<Group mt="md">
<Button onClick={() => handlersRef.current?.decrement()} variant="default">
Decrement by 2
</Button>
<Button onClick={() => handlersRef.current?.increment()} variant="default">
Increment by 2
</Button>
</Group>
</>
);
}
오류 상태 (Error state)
import { NumberInput } from '@mantine/core';
function Demo() {
return (
<>
<NumberInput label="Boolean error" error />
<NumberInput label="With error message" error="Invalid name" mt="md" />
</>
);
}
성공 상태 (Success state)
import { NumberInput } from '@mantine/core';
function Demo() {
return <NumberInput label="Number Input" error="Looks good!" />;
}
비활성 상태 (Disabled state)
import { NumberInput } from '@mantine/core';
function Demo() {
return <NumberInput label="Disabled input" disabled />;
}
Styles API
NumberInput은 Styles API를 지원해요. classNames prop으로 컴포넌트의 내부 요소에 스타일을 추가할 수 있어요.
Styles API 셀렉터:
wrapper– Input의 루트 요소input– 인풋 요소section– 왼쪽·오른쪽 섹션bottomSection– 인풋 테두리 하단 안쪽에 렌더링되는 아래쪽 섹션 요소root– 루트 요소label– 라벨 요소required– 라벨 안에 렌더링되는 필수 별표 요소description– 설명 요소error– 오류 요소success– 성공 요소controls– 증가·감소 버튼 래퍼control– 증가·감소 버튼
요소 ref 가져오기 (Get element ref)
import { useRef } from 'react';
import { NumberInput } from '@mantine/core';
function Demo() {
const ref = useRef<HTMLInputElement>(null);
return <NumberInput ref={ref} />;
}
접근성 (Accessibility)
label prop 없이 NumberInput을 사용하면 스크린 리더가 제대로 알리지 못해요.
// Inaccessible input – screen reader will not announce it properly
import { NumberInput } from '@mantine/core';
function Demo() { return <NumberInput placeholder="Quantity" />; }
aria-label을 설정하면 인풋을 접근 가능하게 만들 수 있어요. 이 경우 라벨은 보이지 않지만 스크린 리더가 알려줘요.
// Accessible input – it has aria-label
import { NumberInput } from '@mantine/core';
function Demo() { return <NumberInput aria-label="Quantity" />; }
label prop이 설정되어 있으면 인풋은 접근 가능하며 aria-label을 설정할 필요가 없어요.
// Accessible input – it has associated label element
import { NumberInput } from '@mantine/core';
function Demo() { return <NumberInput label="Quantity" />; }