Skip to content

Import#

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#

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.

// 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:

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:

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, 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.

Icon Sizes#

Exists in the Icon Library

  • 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:

<Icon icon={star} fill />

See the Icon Library 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 for where, why, and when to use motion, and Motion for visual examples and implementation guidance.

See the 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.

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:

// 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.

<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.
  2. Copy the SVG markup (in Figma, right click -> Copy as -> Copy as SVG).
  3. 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.
  4. Optimize the SVG. Use e.g. Online SVGOMG by using Paste markup.
  5. NB: Do not remove viewBox! The viewBox will together with some CSS ensure that the icon scales based on the root font-size.
  6. Copy again the optimized markup and paste it into your JSX component (inline) or SVG file.
  7. 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#

import ImportedSVGIcon from 'my-icons/custom_icon.svg'
render(<Icon icon={ImportedSVGIcon} />)

Inline the SVG in your JSX#

function CustomSVGIcon(props) {
return <svg {...props}>...</svg>
}
render(<Button icon={CustomSVGIcon} />)

SVG import in Create React App#

import { ReactComponent as CustomIcon } from './custom_icon.svg'
render(<Icon size="medium">{CustomIcon}</Icon>)

Primary Icon#

There is also the IconPrimary component, which comes with all the Primary Icons included in @dnb/eufemia. You do not have to import the primary icons separately.

Related components#

Icon is part of the Content category. Other components for similar needs:

  • Accordion — to let people open and close sections of related content.
  • Avatar — to make a person, company, or profile easier to recognize.
  • Card — to group related content in a clear, separated area.
  • CountryFlag — to show a country by its flag from an ISO country code.
  • DateFormat — to show dates in the correct DNB format.
  • Heading — to create accessible page headings with the correct level.

See all in Content →

Demos#

Default and Medium-sized icons (Responsive)#

<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 for version pinning, CSP, and loading guidance.

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.

<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#

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

<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.

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.

<Button
  variant="tertiary"
  text="Continue"
  icon={<Icon icon={AnimatedArrowRight} animateWhen="hover" />}
/>

Responsive to its inherited font-size#

Heading with auto sized icon

<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

<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.

<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.

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.

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>
)
Suggest an edit