---
title: 'Icon'
description: 'Use Icon to render and size an icon in line with Eufemia styling.'
version: 0.0.0-development
generatedAt: 2026-10-22T16:06:37.118Z
checksum: 36542cfed333c67d1d349b6826ac32a2a7818c1c2f1fa91b6b79d5dd2737d31c
---

# Icon

## Import

```tsx
import { Icon } from '@dnb/eufemia'
```

## Description

The main Icon component is a wrapper for whatever icon you place within it. An imported SVG renders inline. An icon name renders a CSS mask or background image loaded from an SVG URL.

You can use any content you like inside this `Icon` component.

## Relevant links

- [Source code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-eufemia/src/components/icon)
- [Docs code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-design-system-portal/src/docs/uilib/components/icon)

### Why use it?

You will get several advantages when using it, such as:

- Responsiveness in terms of `font-size`
- Coloring
- Accessibility

### Importing Icons

In case your environment does not support tree-shaking, import the icons explicitly.

```jsx
// Named ES import
import { bell } from '@dnb/eufemia/icons'

// or named import with modifier
import { bell as Bell } from '@dnb/eufemia/icons'

// Default and explicit ES import
import Bell from '@dnb/eufemia/icons/bell'
```

### Loading icons by name

Pass an icon name when it comes from application data, a CMS, or an API. No loading property is needed:

```tsx
import { Icon, IconPrimary, Button } from '@dnb/eufemia'

<Icon icon="heart" />
<Icon icon="heart" size="medium" color="var(--token-color-text-action)" />
<IconPrimary icon="bell" />
<Button text="Favorite" icon="heart" />
```

- `Icon` loads string names from the asset CDN without importing an icon catalogue.
- `IconPrimary` renders bundled primary names inline and falls back to the CDN for other names. Components such as Button use the same fallback for string icons.
- Imported SVG components and React elements render directly in both components.

No CDN configuration is needed. Released packages use their installed Eufemia version automatically: version `11.14.1` loads icons from `https://assets.eufemia.dnb.no/v11.14.1/icons/dnb`. Prereleases use their own versioned directory. Local and branch builds use `https://assets.eufemia.dnb.no/icons/dnb`.

If a versioned asset is unavailable while release assets are being published, the icon falls back to the unversioned official CDN URL.

To self-host named icons, copy the SVG files from `@dnb/eufemia/assets/icons/dnb` into a directory served by your app. Then set `iconBaseUrl` on Provider to a same-origin root path starting with `/`, or to an HTTPS directory:

```tsx
import { Button, Icon } from '@dnb/eufemia'
import Provider from '@dnb/eufemia/shared/Provider'

function App() {
  return (
    <Provider iconBaseUrl="/assets/icons">
      <Icon icon="heart" />
      <Button text="Favorite" icon="heart" />
    </Provider>
  )
}
```

The nearest Provider that sets `iconBaseUrl` controls the directory for remote icons. An explicit directory is used as supplied and does not fall back to the official CDN. Invalid directory values are rejected, and remote icon names render nothing.

Direct `Icon` string names use this directory. `IconPrimary` and components that use it, such as Button, continue to render bundled primary names locally.

Set `iconBaseUrl={false}` to disable remote loading, for example when your app must not make requests to other hosts. Imported icons still render. `IconPrimary` and components that use it continue to render bundled primary names; direct `Icon` string names and non-primary string names render nothing, as they did before CDN loading was added.

Use names from the [icon library](/icons), such as `bell`, `arrow_right`, or `bell_medium`. CamelCase and hyphenated names are normalized to underscores. For CDN icons, `size="medium"` selects the `_medium` asset. For example, `<Icon icon="bell" size="medium" />` loads `bell_medium.svg`. An explicit `_medium` suffix is retained. `auto` scales with the surrounding font size and uses the default asset unless the name ends in `_medium`. Bundled primary icons retain their existing size selection.

The browser loads the SVG as a CSS mask and applies `currentColor`, including hover and theme colours. Use `inheritColor={false}` to display the SVG's original colours as an image. Masks and images preserve the SVG's shape: the `fill`, `animate`, `animateWhen`, and `transitionState` features require imported inline SVG icons. Scaling an external SVG also scales its strokes.

The icon reserves its dimensions during loading and renders the same initial markup on the server and client. The browser handles resource caching. If neither official URL is available, or a custom directory cannot provide an icon, its area remains empty. There is no public load or error callback, but in development a console warning names the URL that could not be loaded. Keep essential actions labelled with text, and use imported icons when immediate, offline, or guaranteed rendering is required. Use `alt` for a meaningful standalone icon; decorative icons are hidden from assistive technology by default.

For a Content Security Policy, allow `https://assets.eufemia.dnb.no` in `img-src`. For same-origin self-hosting, allow `'self'`; for a custom cross-origin host, allow that origin instead. A blocked icon stays empty. The CDN supports cross-origin masks through CORS. Custom cross-origin hosts must also return CORS headers when the SVGs are used as masks. Dimensions and mask URLs use React inline styles, so the app's style policy must permit them.

See the [dynamic CDN demos](/uilib/components/icon/demos#dynamic-cdn-icons).

### Icon Sizes

_Exists in the [Icon Library](/icons)_

- **default** `1rem` (16px)
- **medium** `1.5rem` (24px)

_Additional Sizes_

- **small** `0.75rem` (12px)
- **large** `2rem` (32px)
- **x-large** `2.5rem` (40px)
- **xx-large** `3rem` (48px)
- **custom-size** will not be responsive. Width and Height is set as `pixels`

### Filled Icons

Some icons support a filled variant where the SVG paths are filled with `currentColor` instead of being stroked outlines. This is useful for visual emphasis — for example, a filled star to indicate a favorited item, or a filled chevron inside a button.

#### Using the `fill` prop

Set `fill` on an `Icon` to fill its SVG paths with `currentColor`:

```jsx
<Icon icon={star} fill />
```

See the [Icon Library](/icons#filled-icons) for icons that are known to look good when filled.

### Animated Icons

Animated icons are an opt-in enhancement. Use them to reinforce nearby feedback, such as a bell moving once when a new notification arrives. Motion must not be the only indication that something changed.

Read the [Animation Principles](/quickguide-designer/animation-principles/) for where, why, and when to use motion, and [Motion](/quickguide-designer/motion/) for visual examples and implementation guidance.

See the [animated icon demos](#animated-icon-demos) for one-time, replayed, and looping examples.

Animated icons can be used in buttons when the trigger matches the purpose:

- Animate once on hover to invite or preview an action.
- Animate after a click only to acknowledge the interaction itself.
- Animate after a state change to confirm the actual outcome, such as showing an animated check after saving succeeds.
- Loop only while the icon represents genuine ongoing activity.

Animated icons intentionally support direct default imports only. They are not available as named imports from `@dnb/eufemia/icons`. The direct import is the bundle boundary that keeps each icon's animation styles out of applications that do not use it. The icon remains static unless `animate` is enabled.

```jsx
import bell from '@dnb/eufemia/icons/animated/bell'
import arrowRight from '@dnb/eufemia/icons/animated/arrow_right'

<Icon icon={bell} />
<Icon icon={bell} animate />
```

Do not import animated icons from a shared barrel:

```jsx
// Not supported
import { bell } from '@dnb/eufemia/icons/animated'
```

Import `bell_medium` when the icon is displayed at medium size or larger.

Use `animate="loop"` only for genuine ongoing activity, and stop the loop when the activity ends. Change `animationKey` to replay a one-time animation after a new event.

```jsx
<Icon icon={bell} animate animationKey={notificationId} />
<Icon icon={bell} animate={isActive ? 'loop' : false} />
<Icon icon={arrowRight} animateWhen="hover" />
```

The animation is disabled automatically when the user prefers reduced motion.

Use `animateWhen="hover"` to animate an icon when the icon itself, or an interactive parent such as a button or link, is hovered.

### Custom project Icons

For decorative or functional icons (not illustrations), use `SVG` as it gives the user responsiveness and better accessibility. It also gives you more control, so you can change the color and size inherited by the parent HTML element.

To optimize your SVG icons to be used with Eufemia, you can follow these steps or at least get inspired:

1. Make sure your SVG icon fits in the two sizes (default of `16px` and medium of `24px`) with the correct stroke thickness of `1.5px`.
1. **Copy** the SVG markup (in Figma, `right click` -> `Copy as` -> `Copy as SVG`).
1. **Declutter** and remove ID attributes in the markup, so they do not appear twice in your web application DOM. In most cases, you do not need `<defs ... />` and the corresponding ids anyway.
1. **Optimize** the SVG. Use e.g. [Online SVGOMG](https://jakearchibald.github.io/svgomg/) by using `Paste markup`.
1. **NB:** Do not remove `viewBox`! The `viewBox` will together with some CSS ensure that the icon scales based on the root font-size.
1. Copy again the optimized markup and paste it into your JSX component (inline) or SVG file.
1. Consume the custom icons with either dynamic imports (`import(...)`) if you have many icons, or use static imports, like so:

#### If you have an SVG loader

```jsx
import ImportedSVGIcon from 'my-icons/custom_icon.svg'

render(<Icon icon={ImportedSVGIcon} />)
```

#### Inline the SVG in your JSX

```jsx
function CustomSVGIcon(props) {
  return <svg {...props}>...</svg>
}

render(<Button icon={CustomSVGIcon} />)
```

#### SVG import in Create React App

```jsx
import { ReactComponent as CustomIcon } from './custom_icon.svg'

render(<Icon size="medium">{CustomIcon}</Icon>)
```

### Primary Icon

There is also the [IconPrimary](/uilib/components/icon-primary) component, which comes with all the [Primary Icons](/icons/primary) included in `@dnb/eufemia`. You do not have to import the primary icons separately.


## Related components

Icon is part of the [Content](/uilib/components/overview/#content) category. Other components for similar needs:

- [Accordion](/uilib/components/accordion/) – to let people open and close sections of related content.
- [Avatar](/uilib/components/avatar/) – to make a person, company, or profile easier to recognize.
- [Card](/uilib/components/card/) – to group related content in a clear, separated area.
- [CountryFlag](/uilib/components/country-flag/) – to show a country by its flag from an ISO country code.
- [DateFormat](/uilib/components/date-format/) – to show dates in the correct DNB format.
- [Heading](/uilib/components/heading/) – to create accessible page headings with the correct level.

[See all in Content](/uilib/components/overview/#content)


## Demos

### Default and Medium-sized icons (Responsive)


```tsx
<Icon icon={Bell} title="Give Icons a Title, or ..." />
<Icon icon={BellMedium} aria-hidden />
<Bell title="I'm not responsive!" />
{/* <- Not responsive! */}
```


### Dynamic CDN icons

Select a suggested icon or type any name from the icon library. The standalone Icon examples load from the CDN. The Button uses a bundled primary icon when available and otherwise falls back to the CDN. See [dynamic CDN icons](/uilib/components/icon#loading-icons-by-name) for version pinning, CSP, and loading guidance.


```tsx
const iconNames = ['bell', 'bell_medium', 'arrow_right', 'calendar', 'heart', 'star', 'bicycle', 'animal_print'];
const App = () => {
  const [name, setName] = useState('bell');
  return <Flex.Stack>
            <Autocomplete label="Icon name" inputValue="bell" keepValueAndSelection data={iconNames} onType={({
      value
    }) => setName(value)} onChange={({
      data
    }) => {
      if (data) {
        setName(typeof data === 'string' ? data : String(data.content));
      }
    }} />
            <Flex.Horizontal align="center">
              <Icon icon={name} title="Default size" />
              <Icon icon={name} size="medium" color="var(--token-color-text-action)" />
              <Icon icon={name} size="medium" border />
              <Icon icon={name} inheritColor={false} />
              <Button text="Notifications" icon={name} />
            </Flex.Horizontal>
          </Flex.Stack>;
};
render(<App />);
```


### Icons with border

**NB:** Use it with caution. It should not be used where it can confuse users with being a clickable button.


```tsx
render(<Flex.Horizontal align="center">
      <Icon border={true} icon={Bell} />
      <Icon border={true} icon={BellMedium} size="medium" />
      <IconPrimary border={true} icon="information" />
      <IconPrimary border={true} icon="information" size="medium" />
      <Button icon={<IconPrimary icon="add" border />} text="Button" />
    </Flex.Horizontal>)
```


### Filled icons \{#filled-icons-demo\}

Use the `fill` prop on a single icon to fill it.


```tsx
render(<Flex.Stack>
      <Flex.Horizontal align="center">
        <Icon icon={Star} fill />
        <Icon icon={Heart} fill />
        <Avatar icon={<Icon icon={Star} fill />} size="small" hasLabel />
        <Button icon={<Icon icon={Heart} fill />} title="Favorite" />
      </Flex.Horizontal>
    </Flex.Stack>)
```


### Animated icon demos

Animated icons are static by default and only move when you opt in. Import each animated icon directly using its default export. Named imports are intentionally unavailable so each icon's animation code is only included when it is used.

Use a single animation to reinforce an event. Change `animationKey` to replay it while `animate` remains enabled.


```tsx
const App = () => {
  const [animationKey, setAnimationKey] = useState(0);
  return <Flex.Horizontal align="center" gap="small">
            <Icon icon={AnimatedBell} size="medium" animate animationKey={animationKey} aria-hidden />
            <Button variant="secondary" text="Replay" onClick={() => setAnimationKey(key => key + 1)} />
          </Flex.Horizontal>;
};
render(<App />);
```


#### Animated icons in buttons

Use hover to invite or preview an action. Play the animation once and keep the button understandable without motion.


```tsx
render(<Button variant="tertiary" text="Continue" icon={<Icon icon={AnimatedArrowRight} animateWhen="hover" />} />)
```


### Responsive to its inherited `font-size`


```tsx
render(<h4 className="dnb-h--xx-large">
      Heading with auto sized{' '}
      <Icon icon={BellMedium} size="auto" aria-hidden /> icon
    </h4>)
```


### Icon color variations

All of these methods will output the same color


```tsx
<Icon icon={BellMedium} color="var(--color-fire-red)" title="CSS variable" />
<Icon icon={BellMedium} color="#DC2A2A" title="Hex" />
<Icon icon={BellMedium} color="rgb(220,42,42)" title="RGB" />
```


### Icon size variations

The official supported sizes are `default` and `medium`.

**NB:** If you need to use the `large`, `x-large` or `xx-large` sizes, then you should use the `*_medium` version of the icon. Ensure you import the `*_medium` version of the icon.


```tsx
<Icon icon={BellMedium} title="Beach" size="large" />
<Icon icon={BellMedium} title="Beach" size="x-large" />
<Icon icon={BellMedium} title="Beach" size="xx-large" />
```


### Icon transition

Use `Icon.transition()` to animate between SVG icon states. Define named states and use `Icon.transition.activate(element, state)` to switch between them.

When icons have compatible path structures (same number and type of segments), the transition animates via CSS `d` property interpolation. This suits directional variants like `arrow_down` ↔ `arrow_up` or `chevron_down` ↔ `chevron_up`.


```tsx
const directionIcon = Icon.transition({
  down: arrow_down,
  up: arrow_up,
  left: arrow_left,
  right: arrow_right
});
const handleChange = direction => {
  const iconEl = document.querySelector('[data-visual-test="icon-transition"] .dnb-icon') as HTMLElement;
  if (iconEl) {
    Icon.transition.activate(iconEl, direction);
  }
};
render(<>
            <Flex.Horizontal align="center" gap="small">
              <Icon icon={directionIcon} />

              <Field.Selection variant="button" value="down" optionsLayout="horizontal" onChange={handleChange}>
                <Field.Option value="down" title="Down" />
                <Field.Option value="up" title="Up" />
                <Field.Option value="left" title="Left" />
                <Field.Option value="right" title="Right" />
              </Field.Selection>
            </Flex.Horizontal>
            {notSupported}
          </>);
```


### Icon transition fallback

When icons have incompatible path structures (e.g. `question` ↔ `close`), `Icon.transition()` automatically falls back to a transform/opacity crossfade using stacked SVGs. The same `Icon.transition.activate()` API works for both modes.


```tsx
const helpIcon = Icon.transition({
  question,
  close
});
const handleChange = state => {
  const iconEl = document.querySelector('[data-visual-test="icon-transition-fallback"] .dnb-icon') as HTMLElement;
  if (iconEl) {
    Icon.transition.activate(iconEl, state);
  }
};
render(<Flex.Horizontal align="center" gap="small">
            <Icon icon={helpIcon} />

            <Field.Selection variant="button" value="question" optionsLayout="horizontal" onChange={handleChange}>
              <Field.Option value="question" title="Question" />
              <Field.Option value="close" title="Close" />
            </Field.Selection>
          </Flex.Horizontal>);
```

## Properties


```json
{
  "props": {
    "icon": {
      "doc": "A React SVG component or an icon name. Names load from the asset CDN. Set `iconBaseUrl` on [Provider](/uilib/usage/customisation/provider) to use self-hosted SVGs. IconPrimary uses bundled primary icons before falling back to the CDN.",
      "type": [
        "React.ReactNode",
        "string"
      ],
      "status": "optional"
    },
    "title": {
      "doc": "Use a title to provide extra information about the icon used.",
      "type": "string",
      "status": "optional"
    },
    "border": {
      "doc": "Use `true` to display a rounded border with an inherited color. Keep in mind that the icon will have a larger total width and height of `+0.5em`.",
      "type": "boolean",
      "status": "optional"
    },
    "fill": {
      "doc": "If set to `true`, the icon paths will be filled with `currentColor`.",
      "type": "boolean",
      "status": "optional"
    },
    "alt": {
      "doc": "The alternative label (text version) of the icon. Defaults to the imported icon name.",
      "type": "string",
      "status": "optional"
    },
    "size": {
      "doc": "The dimension of the icon. This will be the `viewBox` and represent `width` and `height`. Defaults to `16`. You can use `small`, `medium`, `large` or `auto`. Auto will enable that the icon size gets inherited by the parent HTML element if it provides a `font-size`.",
      "type": [
        "\"default\"",
        "\"small\"",
        "\"medium\"",
        "\"large\"",
        "\"x-large\"",
        "\"xx-large\"",
        "\"basis\"",
        "\"auto\"",
        "number"
      ],
      "status": "optional"
    },
    "color": {
      "doc": "The color can be any valid color property, such as Hex, RGB or preferable – any CSS variable from the [colors table](/uilib/usage/customisation/colors), e.g. `var(--color-ocean-green)`. Defaults to no color, which means `--color-black-80`.",
      "type": "Various",
      "status": "optional"
    },
    "inheritColor": {
      "doc": "Defaults to `true`. Set to `false` if you do not want to inherit the color by `currentColor`.",
      "type": "boolean",
      "status": "optional"
    },
    "modifier": {
      "doc": "Modifier class to define. Will result in: `dnb-icon--${modifier}`.",
      "type": "string",
      "status": "optional"
    },
    "animate": {
      "doc": "Plays motion provided by an explicitly imported animated icon. Use `true` or `once` to play once, or `loop` to repeat the animation.",
      "type": [
        "boolean",
        "\"once\"",
        "\"loop\""
      ],
      "status": "optional"
    },
    "animateWhen": {
      "doc": "Plays an animated icon when the icon itself, or an interactive parent such as a button or link, is hovered.",
      "type": "\"hover\"",
      "status": "optional"
    },
    "animationKey": {
      "doc": "Change this value to replay an animated icon while `animate` remains enabled.",
      "type": [
        "string",
        "number"
      ],
      "status": "optional"
    },
    "skeleton": {
      "doc": "If set to `true`, an overlaying skeleton with animation will be shown.",
      "type": "boolean",
      "status": "optional"
    },
    "[Space](/uilib/layout/space/properties)": {
      "doc": "Spacing properties like `top` or `bottom` are supported.",
      "type": [
        "string",
        "object"
      ],
      "status": "optional"
    }
  }
}
```
