suiss/ui
  • Docs
  • Components
  • Patterns
  • Interactions
  • Charts
  • Blocks
GitHubGet started
DocsComponentsPatternsInteractionsChartsBlocks
Components82
  • Overview
  • Accordion
  • Alert
  • Alert Dialog
  • Alert Sheet
  • Amount Input
  • Aspect Ratio
  • Attachment
  • Avatar
  • Badge
  • Bottom Bar
  • Breadcrumb
  • Bubble
  • Button
  • Button Group
  • Calendar
  • Card
  • Carousel
  • Chart
  • Checkbox
  • Chip
  • Collapsible
  • Combobox
  • Command
  • Context Menu
  • Dialog
  • Direction
  • Drawer
  • Dropdown Menu
  • Empty
  • FAB
  • Field
  • Fullscreen Menu
  • Haptics
  • Header
  • Hover Card
  • Icon
  • Input
  • Input Accessory Bar
  • Input Group
  • Input OTP
  • Item
  • Kbd
  • Keyboard Scroll View
  • Keypad
  • Label
  • Marker
  • Menubar
  • Message
  • Message Scroller
  • Native Select
  • Navigation Menu
  • Page Indicator
  • Pagination
  • Pin Pad
  • Popover
  • Progress
  • QR Scanner
  • Questionnaire
  • Radio Group
  • Resizable
  • Screen
  • Scroll Area
  • Select
  • Separator
  • Sheet
  • Sidebar
  • Signature Pad
  • Skeleton
  • Slider
  • Spinner
  • Switch
  • Tab Bar
  • Table
  • Tabs
  • Text
  • Textarea
  • Toast
  • Toggle
  • Toggle Group
  • Tooltip
  • Wheel Picker
  • Widget

suiss/ui · Component

Icon

Renders a Phosphor icon with size and color set through class names.

Icon is an open-source component from suiss/ui for React Native, SwiftUI and Jetpack Compose. The @esuiss/ui CLI copies its source into your project, so you own the code.

iOS
Icon on iOS in light modeIcon on iOS in dark mode
import { StarIcon } from "phosphor-react-native"
import { Icon } from "@/components/ui/icon"

export function IconExample() {
  return (
    <Icon as={StarIcon} />
  )
}
import EsuissUI
import PhosphorSwift
import SwiftUI

struct IconExample: View {
  var body: some View {
    IconGlyph(image: Ph.star.regular)
  }
}

struct IconGlyph: View {
  let image: Image
  var size: CGFloat = 20
  var color: Color = EsuissColor.foreground

  var body: some View {
    image
      .resizable()
      .scaledToFit()
      .frame(width: size, height: size)
      .foregroundStyle(color)
  }
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.unit.dp
import com.adamglin.PhosphorIcons
import com.adamglin.phosphoricons.Regular
import com.adamglin.phosphoricons.regular.Star
import com.esuiss.ui.components.Icon

@Composable
fun IconRow(content: @Composable () -> Unit) {
    Row(horizontalArrangement = Arrangement.spacedBy(16.dp), verticalAlignment = Alignment.CenterVertically) {
        content()
    }
}

@Composable
fun IconDefault() {
    IconRow {
        Icon(PhosphorIcons.Regular.Star, contentDescription = null)
    }
}

Icon takes a Phosphor icon component in as and maps size-* classes to its size and text-* classes to its color. It defaults to size-5 text-foreground.

Icons inherit text and icon classes from their parent, so an Icon inside a Button, Badge or menu item picks up the right size and color without extra classes. Components provide these through IconClassContext.

SwiftUI has no Icon view to install. Use the images from the Phosphor Swift package directly, and set their size and color with SwiftUI modifiers, as the examples on this page do.

Installation

$ pnpm dlx shadcn@latest add @esuiss-rn/icon

See Installation for the components.json setup. Or use the suiss/ui CLI:

$ pnpm dlx @esuiss/ui@latest add icon
$ pnpm dlx @esuiss/ui@latest add icon --platform kotlin

Add the Phosphor Swift package to your app:

Package.swift
.package(url: "https://github.com/phosphor-icons/swift", from: "2.1.0")

The CLI also adds what it builds on: Text.

Manual

  1. Install the dependencies:
npx expo install cn phosphor-react-native react-native-svg uniwind
  1. Add the pieces it builds on: Text.

  2. Copy the source into components/ui/icon.tsx:

uniwind/components/ui/icon.tsx
import { cn } from "cn"
import type {
  Icon as PhosphorIcon,
  IconProps as PhosphorIconProps,
} from "phosphor-react-native"
import * as React from "react"
import { withUniwind } from "uniwind"
import { TextClassContext } from "@/components/ui/text"

type IconProps = PhosphorIconProps & {
  as: PhosphorIcon
  className?: string
}

const IconClassContext = React.createContext<string | undefined>(undefined)
  1. Update the import paths to match your project.
  1. Install the dependencies:
npx expo install cn nativewind phosphor-react-native react-native-svg
  1. Add the pieces it builds on: Text.

  2. Copy the source into components/ui/icon.tsx:

nativewind/components/ui/icon.tsx
import { cn } from "cn"
import { cssInterop } from "nativewind"
import type {
  Icon as PhosphorIcon,
  IconProps as PhosphorIconProps,
} from "phosphor-react-native"
import * as React from "react"
import { TextClassContext } from "@/components/ui/text"

type IconProps = PhosphorIconProps & {
  as: PhosphorIcon
  className?: string
}

const IconClassContext = React.createContext<string | undefined>(undefined)
  1. Update the import paths to match your project.
  1. Install the dependencies:
npx expo install phosphor-react-native react-native-svg
  1. Add the pieces it builds on: Text.

  2. Copy the source into components/ui/icon.tsx:

stylesheet/components/ui/icon.tsx
import type {
  Icon as PhosphorIcon,
  IconProps as PhosphorIconProps,
} from "phosphor-react-native"
import * as React from "react"
import { TextStyleContext } from "@/components/ui/text"
import { useTheme } from "@/components/ui/theme"

type IconProps = PhosphorIconProps & {
  as: PhosphorIcon
}

const IconSizeContext = React.createContext<number | undefined>(undefined)

function Icon({ as: IconComponent, size, color, ...props }: IconProps) {
  const { colors } = useTheme()
  1. Update the import paths to match your project.
  1. Set minSdk to 24 or higher and add the dependencies:
build.gradle.kts
implementation(platform("androidx.compose:compose-bom:2026.09.00"))
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.ui:ui")
  1. Copy the theme files every view uses:
kotlin/theme/Theme.kt
package com.esuiss.ui.theme

import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.dropShadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.shadow.Shadow as DropShadow
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.LineHeightStyle
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
kotlin/theme/Tokens.kt
package com.esuiss.ui.theme

import androidx.compose.runtime.Immutable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

@Immutable
data class Colors(
    val background: Color,
    val foreground: Color,
    val card: Color,
    val cardForeground: Color,
    val popover: Color,
    val popoverForeground: Color,
    val primary: Color,
    val primaryForeground: Color,
  1. Copy the source and every file it uses:
kotlin/components/Icon.kt
package com.esuiss.ui.components

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.Dp
import com.esuiss.ui.theme.EsuissTheme

@Composable
fun Icon(
    imageVector: ImageVector,
kotlin/components/Content.kt
package com.esuiss.ui.components

import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

val LocalContentStyle = compositionLocalOf { TextStyle.Default }

val LocalContentColor = compositionLocalOf { Color.Unspecified }

val LocalIconSize = compositionLocalOf { 20.dp }
  1. Update the com.esuiss.ui package names to match your project.

Usage

import { Icon } from "@/components/ui/icon"
<Icon as={StarIcon} />

Pass the icon component, not an element: <Icon as={HeartIcon} />. Override the size and color with className, such as size-6 text-destructive. Other Phosphor props, such as weight and mirrored, are passed through.

Wrap a subtree in IconClassContext.Provider to set default classes for the icons inside your own components.

Examples

Sizes

Icons at several size-* classes.

iOS
Icon (sizes) on iOS in light modeIcon (sizes) on iOS in dark mode
import { BellIcon } from "phosphor-react-native"
import { View } from "react-native"
import { Icon } from "@/components/ui/icon"

export function IconSizesExample() {
  return (
    <View className="flex-row items-center gap-4">
      <Icon as={BellIcon} className="size-4" />
      <Icon as={BellIcon} />
      <Icon as={BellIcon} className="size-6" />
      <Icon as={BellIcon} className="size-8" />
    </View>
  )
}
import EsuissUI
import PhosphorSwift
import SwiftUI

struct IconSizesExample: View {
  var body: some View {
    HStack(spacing: 16) {
      IconGlyph(image: Ph.bell.regular, size: 16)
      IconGlyph(image: Ph.bell.regular)
      IconGlyph(image: Ph.bell.regular, size: 24)
      IconGlyph(image: Ph.bell.regular, size: 32)
    }
  }
}

struct IconGlyph: View {
  let image: Image
  var size: CGFloat = 20
  var color: Color = EsuissColor.foreground

  var body: some View {
    image
      .resizable()
      .scaledToFit()
      .frame(width: size, height: size)
      .foregroundStyle(color)
  }
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.unit.dp
import com.adamglin.PhosphorIcons
import com.adamglin.phosphoricons.Regular
import com.adamglin.phosphoricons.regular.Bell
import com.esuiss.ui.components.Icon

@Composable
fun IconRow(content: @Composable () -> Unit) {
    Row(horizontalArrangement = Arrangement.spacedBy(16.dp), verticalAlignment = Alignment.CenterVertically) {
        content()
    }
}

@Composable
fun IconSizes() {
    IconRow {
        Icon(PhosphorIcons.Regular.Bell, contentDescription = null, size = 16.dp)
        Icon(PhosphorIcons.Regular.Bell, contentDescription = null)
        Icon(PhosphorIcons.Regular.Bell, contentDescription = null, size = 24.dp)
        Icon(PhosphorIcons.Regular.Bell, contentDescription = null, size = 32.dp)
    }
}

Colors

Icons in different color tokens.

iOS
Icon (colors) on iOS in light modeIcon (colors) on iOS in dark mode
import { GearIcon, HeartIcon, InfoIcon, WarningIcon } from "phosphor-react-native"
import { View } from "react-native"
import { Icon } from "@/components/ui/icon"

export function IconColorsExample() {
  return (
    <View className="flex-row items-center gap-4">
      <Icon as={InfoIcon} />
      <Icon as={GearIcon} className="text-muted-foreground" />
      <Icon as={HeartIcon} className="text-primary" weight="fill" />
      <Icon as={WarningIcon} className="text-destructive" />
    </View>
  )
}
import EsuissUI
import PhosphorSwift
import SwiftUI

struct IconColorsExample: View {
  var body: some View {
    HStack(spacing: 16) {
      IconGlyph(image: Ph.info.regular)
      IconGlyph(image: Ph.gear.regular, color: EsuissColor.mutedForeground)
      IconGlyph(image: Ph.heart.fill, color: EsuissColor.primary)
      IconGlyph(image: Ph.warning.regular, color: EsuissColor.destructive)
    }
  }
}

struct IconGlyph: View {
  let image: Image
  var size: CGFloat = 20
  var color: Color = EsuissColor.foreground

  var body: some View {
    image
      .resizable()
      .scaledToFit()
      .frame(width: size, height: size)
      .foregroundStyle(color)
  }
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.unit.dp
import com.adamglin.PhosphorIcons
import com.adamglin.phosphoricons.Fill
import com.adamglin.phosphoricons.Regular
import com.adamglin.phosphoricons.regular.Gear
import com.adamglin.phosphoricons.regular.Info
import com.adamglin.phosphoricons.regular.Warning
import com.esuiss.ui.components.Icon
import com.esuiss.ui.theme.EsuissTheme

@Composable
fun IconRow(content: @Composable () -> Unit) {
    Row(horizontalArrangement = Arrangement.spacedBy(16.dp), verticalAlignment = Alignment.CenterVertically) {
        content()
    }
}

@Composable
fun IconColors() {
    val colors = EsuissTheme.colors
    IconRow {
        Icon(PhosphorIcons.Regular.Info, contentDescription = null)
        Icon(PhosphorIcons.Regular.Gear, contentDescription = null, tint = colors.mutedForeground)
        Icon(PhosphorIcons.Fill.FillHeart, contentDescription = null, tint = colors.primary)
        Icon(PhosphorIcons.Regular.Warning, contentDescription = null, tint = colors.destructive)
    }
}

Weights

The available Phosphor weights.

iOS
Icon (weights) on iOS in light modeIcon (weights) on iOS in dark mode
import { StarIcon } from "phosphor-react-native"
import { View } from "react-native"
import { Icon } from "@/components/ui/icon"

export function IconWeightsExample() {
  return (
    <View className="flex-row items-center gap-4">
      <Icon as={StarIcon} className="size-6" weight="thin" />
      <Icon as={StarIcon} className="size-6" weight="light" />
      <Icon as={StarIcon} className="size-6" weight="regular" />
      <Icon as={StarIcon} className="size-6" weight="bold" />
      <Icon as={StarIcon} className="size-6" weight="fill" />
      <Icon as={StarIcon} className="size-6" weight="duotone" />
    </View>
  )
}
import EsuissUI
import PhosphorSwift
import SwiftUI

struct IconWeightsExample: View {
  var body: some View {
    HStack(spacing: 16) {
      IconGlyph(image: Ph.star.thin, size: 24)
      IconGlyph(image: Ph.star.light, size: 24)
      IconGlyph(image: Ph.star.regular, size: 24)
      IconGlyph(image: Ph.star.bold, size: 24)
      IconGlyph(image: Ph.star.fill, size: 24)
      IconGlyph(image: Ph.star.duotone, size: 24)
    }
  }
}

struct IconGlyph: View {
  let image: Image
  var size: CGFloat = 20
  var color: Color = EsuissColor.foreground

  var body: some View {
    image
      .resizable()
      .scaledToFit()
      .frame(width: size, height: size)
      .foregroundStyle(color)
  }
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.unit.dp
import com.adamglin.PhosphorIcons
import com.adamglin.phosphoricons.Bold
import com.adamglin.phosphoricons.Duotone
import com.adamglin.phosphoricons.Fill
import com.adamglin.phosphoricons.Light
import com.adamglin.phosphoricons.Regular
import com.adamglin.phosphoricons.Thin
import com.adamglin.phosphoricons.regular.Star
import com.esuiss.ui.components.Icon

@Composable
fun IconRow(content: @Composable () -> Unit) {
    Row(horizontalArrangement = Arrangement.spacedBy(16.dp), verticalAlignment = Alignment.CenterVertically) {
        content()
    }
}

@Composable
fun IconWeights() {
    IconRow {
        Icon(PhosphorIcons.Thin.ThinStar, contentDescription = null, size = 24.dp)
        Icon(PhosphorIcons.Light.LightStar, contentDescription = null, size = 24.dp)
        Icon(PhosphorIcons.Regular.Star, contentDescription = null, size = 24.dp)
        Icon(PhosphorIcons.Bold.BoldStar, contentDescription = null, size = 24.dp)
        Icon(PhosphorIcons.Fill.FillStar, contentDescription = null, size = 24.dp)
        Icon(PhosphorIcons.Duotone.DuotoneStar, contentDescription = null, size = 24.dp)
    }
}

API Reference

01

Icon

Styled Phosphor icon.

PropTypeDefaultDescription
asPhosphorIconRequiredThe Phosphor icon component to render.

Also accepts the props of IconProps.

Accessibility

  • Icons have no label of their own. When an icon is the only content of a pressable, give the pressable an accessibilityLabel.

Notes

  • The default color is the text-foreground token, so icons follow light and dark mode with the system appearance.
NextInputPreviousHover Card

On this page

  1. 01Installation
    • Manual
  2. 02Usage
  3. 03Examples
    • Sizes
    • Colors
    • Weights
  4. 04API Reference
    • Icon
  5. 05Accessibility
  6. 06Notes