Historgram
freeHistorgram draws a value distribution as bars and highlights whichever sub-range currently matches a paired range control.
@floating-ui/react
Preview
Dark
$100 – $350
import { useState } from 'react';
import Historgram from '@/components/composites/Historgram';
import Slider from '@/components/ui/Slider';
const priceData = [
{ value: 1, label: '$0-12' },
{ value: 1, label: '$12-25' },
{ value: 2, label: '$25-37' },
{ value: 2, label: '$37-50' },
{ value: 4, label: '$50-62' },
{ value: 5, label: '$62-75' },
{ value: 6, label: '$75-87' },
{ value: 8, label: '$87-100' },
{ value: 12, label: '$100-112' },
{ value: 14, label: '$112-125' },
{ value: 16, label: '$125-137' },
{ value: 19, label: '$137-150' },
{ value: 25, label: '$150-162' },
{ value: 28, label: '$162-175' },
{ value: 31, label: '$175-187' },
{ value: 33, label: '$187-200' },
{ value: 37, label: '$200-212' },
{ value: 38, label: '$212-225' },
{ value: 39, label: '$225-237' },
{ value: 40, label: '$237-250' },
{ value: 40, label: '$250-262' },
{ value: 39, label: '$262-275' },
{ value: 38, label: '$275-287' },
{ value: 37, label: '$287-300' },
{ value: 33, label: '$300-312' },
{ value: 31, label: '$312-325' },
{ value: 28, label: '$325-337' },
{ value: 25, label: '$337-350' },
{ value: 19, label: '$350-362' },
{ value: 16, label: '$362-375' },
{ value: 14, label: '$375-387' },
{ value: 12, label: '$387-400' },
{ value: 8, label: '$400-412' },
{ value: 6, label: '$412-425' },
{ value: 5, label: '$425-437' },
{ value: 4, label: '$437-450' },
{ value: 2, label: '$450-462' },
{ value: 2, label: '$462-475' },
{ value: 1, label: '$475-487' },
{ value: 1, label: '$487-500' },
];
export default function UsageDemo() {
const [range, setRange] = useState<[number, number]>([100, 350]);
return (
<div className="w-full max-w-md">
<Historgram data={priceData} range={range} min={0} max={500} />
<div className="mt-4 space-y-2">
<Slider.Range min={0} max={500} value={range} onChange={setRange} />
<div className="text-xs text-muted-foreground">
${range[0]} – ${range[1]}
</div>
</div>
</div>
);
}Installation
Add this component with the NateUI CLI.
npx nateui@latest add HistorgramExamples
Price Filter
Preview
Dark
import { useState } from 'react';
import Form from '@/components/ui/Form';
import Slider from '@/components/ui/Slider';
import FormatInput from '@/components/composites/FormatInput';
import Historgram from '@/components/composites/Historgram';
const priceData = [
{ value: 1, label: '$0-12' },
{ value: 1, label: '$12-25' },
{ value: 2, label: '$25-37' },
{ value: 2, label: '$37-50' },
{ value: 4, label: '$50-62' },
{ value: 5, label: '$62-75' },
{ value: 6, label: '$75-87' },
{ value: 8, label: '$87-100' },
{ value: 12, label: '$100-112' },
{ value: 14, label: '$112-125' },
{ value: 16, label: '$125-137' },
{ value: 19, label: '$137-150' },
{ value: 25, label: '$150-162' },
{ value: 28, label: '$162-175' },
{ value: 31, label: '$175-187' },
{ value: 33, label: '$187-200' },
{ value: 37, label: '$200-212' },
{ value: 38, label: '$212-225' },
{ value: 39, label: '$225-237' },
{ value: 40, label: '$237-250' },
{ value: 40, label: '$250-262' },
{ value: 39, label: '$262-275' },
{ value: 38, label: '$275-287' },
{ value: 37, label: '$287-300' },
{ value: 33, label: '$300-312' },
{ value: 31, label: '$312-325' },
{ value: 28, label: '$325-337' },
{ value: 25, label: '$337-350' },
{ value: 19, label: '$350-362' },
{ value: 16, label: '$362-375' },
{ value: 14, label: '$375-387' },
{ value: 12, label: '$387-400' },
{ value: 8, label: '$400-412' },
{ value: 6, label: '$412-425' },
{ value: 5, label: '$425-437' },
{ value: 4, label: '$437-450' },
{ value: 2, label: '$450-462' },
{ value: 2, label: '$462-475' },
{ value: 1, label: '$475-487' },
{ value: 1, label: '$487-500' },
];
export default function PriceFilterDemo() {
const [range, setRange] = useState<[number, number]>([50, 300]);
return (
<div className="w-full max-w-md">
<Historgram data={priceData} range={range} min={0} max={500} />
<div className="mt-4">
<Slider.Range min={0} max={500} value={range} onChange={setRange} />
</div>
<div className="mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2">
<Form.Field label="Min" htmlFor="price-min">
<FormatInput.Numeric
id="price-min"
prefix="$"
thousandSeparator
value={range[0]}
onValueChange={(values) =>
setRange([values.floatValue ?? 0, range[1]])
}
/>
</Form.Field>
<Form.Field label="Max" htmlFor="price-max">
<FormatInput.Numeric
id="price-max"
prefix="$"
thousandSeparator
value={range[1]}
onValueChange={(values) =>
setRange([range[0], values.floatValue ?? 0])
}
/>
</Form.Field>
</div>
</div>
);
}Popover
Preview
Dark
import { useState } from 'react';
import { PiFunnel } from 'react-icons/pi'
import Button from '@/components/ui/Button';
import Popover from '@/components/ui/Popover';
import Form from '@/components/ui/Form';
import Slider from '@/components/ui/Slider';
import FormatInput from '@/components/composites/FormatInput';
import Historgram from '@/components/composites/Historgram';
const priceData = [
{ value: 2, label: '$0-25' },
{ value: 4, label: '$25-50' },
{ value: 7, label: '$50-75' },
{ value: 11, label: '$75-100' },
{ value: 15, label: '$100-125' },
{ value: 20, label: '$125-150' },
{ value: 25, label: '$150-175' },
{ value: 29, label: '$175-200' },
{ value: 33, label: '$200-225' },
{ value: 35, label: '$225-250' },
{ value: 34, label: '$250-275' },
{ value: 31, label: '$275-300' },
{ value: 27, label: '$300-325' },
{ value: 22, label: '$325-350' },
{ value: 17, label: '$350-375' },
{ value: 13, label: '$375-400' },
{ value: 9, label: '$400-425' },
{ value: 6, label: '$425-450' },
{ value: 3, label: '$450-475' },
{ value: 1, label: '$475-500' },
];
export default function PopoverDemo() {
const [range, setRange] = useState<[number, number]>([100, 350]);
return (
<div className="flex justify-center">
<Popover
renderTrigger={<Button icon={<PiFunnel />}>Filter</Button>}
width={300}
>
<div className="flex flex-col gap-4">
<Historgram data={priceData} range={range} min={0} max={500} />
<Slider.Range min={0} max={500} value={range} onChange={setRange} />
<div className="grid grid-cols-2 gap-4">
<Form.Field label="Min" htmlFor="popover-price-min">
<FormatInput.Numeric
id="popover-price-min"
prefix="$"
thousandSeparator
value={range[0]}
onValueChange={(values) =>
setRange([values.floatValue ?? 0, range[1]])
}
/>
</Form.Field>
<Form.Field label="Max" htmlFor="popover-price-max">
<FormatInput.Numeric
id="popover-price-max"
prefix="$"
thousandSeparator
value={range[1]}
onValueChange={(values) =>
setRange([range[0], values.floatValue ?? 0])
}
/>
</Form.Field>
</div>
</div>
</Popover>
</div>
);
}API
Historgram is presentational — its bars have pointer-events: none and cannot be clicked or dragged directly. Pair it with an interactive range control such as Slider.Range (see Usage) so range updates from user input.
| Prop | Description | Type | Default |
|---|---|---|---|
data | Bars to render, one per bin. label is part of the shape but not currently rendered by the component. | { value: number; label: string }[] | — |
range | Currently highlighted sub-range; bars whose bin center falls inside it render in the active color. | [number, number] | — |
min | Lower bound of the domain data spans. | number | — |
max | Upper bound of the domain data spans. | number | — |
className | Class names applied to every bar. Overriding a bg-* utility recolors all bars uniformly and loses the active/inactive distinction; shape or spacing utilities like rounded-* and mx-* override safely. | string | — |