Skip to content

Import#

import { Icon } from '@dnb/eufemia'

Description#

The main Icon component is a wrapper for whatever icon you place within it. This means a span wrapping an inline SVG.

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'

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! */}

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#

h1 with auto sized icon

<h1 className="dnb-h--xx-large">
  h1 with auto sized <Icon icon={BellMedium} size="auto" aria-hidden />{' '}
  icon
</h1>

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_downarrow_up or chevron_downchevron_up.

Your browser does not support CSS d path interpolation, so the icon transition will fall back to a simple crossfade.
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. questionclose), 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