Icon
Overviewimport { Icon, IconComponent } from '@nili/ui';This is the library’s own overview page, rendered live. Switch the language or the theme in the header and it follows.
Basic
Pass the icon component itself to icon, or an already-created element as children. Both forms exist because a call site that already holds an element should not have to unwrap it.
import { Icon } from "@nili/ui"
import { RiSearchLine } from "@remixicon/react"
export function Example() {
return (
<>
{/* the component */}
<Icon icon={RiSearchLine} />
{/* or an element */}
<Icon>
<RiSearchLine />
</Icon>
</>
)
}Sizes
size takes a number of pixels or any CSS length. '1em' makes the glyph track the surrounding font size, which is what you want for an icon sitting inside a line of text.
Search
Search
Search
Search
import { Icon } from "@nili/ui"
import { RiSearchLine } from "@remixicon/react"
export function Example() {
return (
<>
<Icon icon={RiSearchLine} size={16} />
<Icon icon={RiSearchLine} size={24} />
<Icon icon={RiSearchLine} size={40} />
{/* follows the text around it */}
<p className="text-[20px]">
{t('sizes.search')} <Icon icon={RiSearchLine} size="1em" />
</p>
</>
)
}Colour
An icon paints itself with currentColor, so it inherits from whatever it sits in — which is why it works inside a Button or a Badge with no colour prop at all. Prefer a text-* token class over the color escape hatch.
import { Icon, Button, Badge } from "@nili/ui"
import { RiCheckLine } from "@remixicon/react"
export function Example() {
return (
<>
{/* inherits */}
<span className="text-success-base">
<Icon icon={RiCheckLine} />
</span>
{/* inside a control, with nothing to say */}
<Button startIcon={<Icon icon={RiCheckLine} />}>
{t('colour.approve')}
</Button>
<Badge color="green" startIcon={<Icon icon={RiCheckLine} />}>Done</Badge>
</>
)
}Mirroring
mirrored flips the glyph horizontally under RTL. Set it on directional icons — arrows, chevrons, a send plane — and leave it off everything else: a mirrored clock or magnifying glass is simply wrong, not localised.
import { Icon } from "@nili/ui"
import { RiArrowRightSLine, RiSearchLine } from "@remixicon/react"
export function Example() {
return (
<>
{/* directional — flips in Persian */}
<Icon icon={RiArrowRightSLine} mirrored />
{/* not directional — must not flip */}
<Icon icon={RiSearchLine} />
</>
)
}Accessible names
An icon is aria-hidden by default, because almost every icon sits beside text that already says what it means — and an icon that repeats the label makes a screen reader say it twice. Pass label only when the glyph is the whole message.
import { Icon } from "@nili/ui"
import { RiCheckLine } from "@remixicon/react"
export function Example() {
return (
<>
{/* decorative: the word "Approved" is right there */}
<span>
<Icon icon={RiCheckLine} /> {t('names.approved')}
</span>
{/* meaningful: nothing else says this row passed */}
<Icon icon={RiCheckLine} label={t('names.passed')} />
</>
)
}Icon
extends Omit< SVGProps<SVGSVGElement>, 'color' | 'children' | 'ref' >
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| icon | IconComponent | — | The icon component itself, e.g. `RiAddLine` from `@remixicon/react`. |
| children | ReactNode | — | Alternative to `icon`: pass an already-created element as a child. |
| size | number | string | 24 | Pixel size, or any CSS length. `'1em'` makes the icon track font size. |
| color | string | — | Overrides `currentColor`. Prefer a `text-*` token class instead. |
| label | string | — | Accessible name. Provide this ONLY when the icon carries meaning on its own; otherwise the icon stays `aria-hidden` and the adjacent text speaks. |
| mirrored | boolean | false | Flip horizontally in RTL. Set on directional glyphs (arrows, chevrons). |
IconRender
internalextends Omit< SVGProps<SVGSVGElement>, 'color' | 'children' | 'ref' >
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| color | string | — | |
| size | number | string | — | |
| children | never | — |