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

Label

An accessible text label that names a form control.

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

import { Label } from "@/components/ui/label"

export default function LabelDemo() {
  return <Label>Email address</Label>
}

Label renders a native label element with the library's form typography. Associate it with a control through htmlFor, or nest the control inside it, so clicking the label focuses or toggles the control.

Use Label for standalone controls. Inside a Field layout, prefer the field's own label part when one is provided.

Installation

CLI

$ pnpm dlx shadcn@latest add @esuiss/label

Works in any project with a components.json. See Installation for the one-line registry setup. Or use the suiss/ui CLI:

$ pnpm dlx @esuiss/ui@latest add label

Manual

  1. Add the cn helper.

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

components/ui/label.tsx
"use client"

import type * as React from "react"
import { cn } from "@/lib/utils"

function Label({ className, ...props }: React.ComponentProps<"label">) {
  return (
    <label
      data-slot="label"
      className={cn(
        "flex items-center gap-2 text-base font-semibold select-none has-[>[data-slot=checkbox]]:font-normal has-[>[data-slot=radio-group-item]]:font-normal has-[>[data-slot=switch]]:font-normal peer-data-[slot=checkbox]:font-normal peer-data-[slot=radio-group-item]:font-normal peer-data-[slot=switch]:font-normal group-data-[disabled=true]:pointer-events-none peer-disabled:cursor-not-allowed group-data-[disabled=true]:opacity-50 peer-disabled:opacity-50",
        className
      )}
      {...props}
    />
  )
  1. Update the import paths to match your project.

Usage

import { Label } from "@/components/ui/label"
<Label>Email address</Label>

Give the control an id and pass the same value to htmlFor. Next to a checkbox, radio item or switch, the label automatically switches to regular weight.

Examples

With input

A label linked to a text input; clicking the label focuses the input.

import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"

export default function LabelWithInput() {
  return (
    <div className="flex w-72 flex-col gap-2">
      <Label htmlFor="label-email">Email address</Label>
      <Input id="label-email" type="email" placeholder="[email protected]" />
    </div>
  )
}

With checkbox

A label placed after a checkbox; clicking the label toggles it.

import { Checkbox } from "@/components/ui/checkbox"
import { Label } from "@/components/ui/label"

export default function LabelWithCheckbox() {
  return (
    <div className="flex items-center gap-2">
      <Checkbox id="label-terms" />
      <Label htmlFor="label-terms">Accept terms and conditions</Label>
    </div>
  )
}

Disabled

A label shown in its dimmed style inside a disabled group, above a disabled input.

import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"

export default function LabelDisabled() {
  return (
    <div className="group flex w-72 flex-col gap-2" data-disabled="true">
      <Label htmlFor="label-disabled">Email address</Label>
      <Input
        id="label-disabled"
        type="email"
        placeholder="[email protected]"
        disabled
      />
    </div>
  )
}

API Reference

01

Label

Renders a label element that names and activates its associated control.

Also accepts the props of React.ComponentProps<"label">.

Accessibility

  • The label text becomes the control's accessible name when linked with htmlFor or by nesting.
  • Clicking the label focuses a text input or toggles a checkbox, matching native label behavior.

Notes

  • Styling hook: data-slot="label".
  • The label dims to opacity-50 when it follows a disabled peer control, or when an ancestor with the group class has data-disabled="true".
  • Font weight drops to normal when the label contains, or follows, a checkbox, radio-group-item or switch slot.
iOS
Label on iOS in light modeLabel on iOS in dark mode
import { Label } from "@/components/ui/label"

export function LabelExample() {
  return (
    <Label>Your email address</Label>
  )
}
import EsuissUI
import SwiftUI

struct LabelExample: View {
  var body: some View {
    FieldLabel("Your email address")
  }
}
import androidx.compose.runtime.Composable
import com.esuiss.ui.components.Label

@Composable
fun LabelExample() {
    Label("Your email address")
}

Label renders a Text inside a Pressable row. The pressable accepts onPress, onLongPress, onPressIn and onPressOut, so pressing the label can toggle or focus the control next to it. A disabled label is dimmed to 50% opacity.

Installation

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

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

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

Manual

  1. Install the dependencies:
npx expo install cn
  1. Add the pieces it builds on: the primitives components/primitives/label.tsx, components/primitives/use-render.tsx.

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

uniwind/components/ui/label.tsx
import { cn } from "cn"
import * as LabelPrimitive from "@/components/primitives/label"
import { resolveClassName } from "@/components/primitives/use-render"

function Label({
  className,
  onPress,
  onLongPress,
  onPressIn,
  onPressOut,
  disabled,
  ...props
}: React.ComponentProps<typeof LabelPrimitive.Text>) {
  return (
    <LabelPrimitive.Root
      className="flex-row items-center gap-2"
  1. Update the import paths to match your project.
  1. Install the dependencies:
npx expo install cn
  1. Add the pieces it builds on: the primitives components/primitives/label.tsx, components/primitives/use-render.tsx.

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

nativewind/components/ui/label.tsx
import { cn } from "cn"
import * as LabelPrimitive from "@/components/primitives/label"
import { resolveClassName } from "@/components/primitives/use-render"

function Label({
  className,
  onPress,
  onLongPress,
  onPressIn,
  onPressOut,
  disabled,
  ...props
}: React.ComponentProps<typeof LabelPrimitive.Text>) {
  return (
    <LabelPrimitive.Root
      className="flex-row items-center gap-2"
  1. Update the import paths to match your project.
  1. Add the pieces it builds on: the primitives components/primitives/label.tsx, components/primitives/use-render.tsx.

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

stylesheet/components/ui/label.tsx
import { StyleSheet } from "react-native"
import * as LabelPrimitive from "@/components/primitives/label"
import { fontSize, useTheme } from "@/components/ui/theme"

const styles = StyleSheet.create({
  root: {
    display: "flex",
    flexDirection: "row",
    alignItems: "center",
    gap: 8,
  },
  text: { ...fontSize.base, fontWeight: "600" },
})

function Label({
  style,
  1. Update the import paths to match your project.
  1. Target iOS 17 or later.

  2. Copy the theme files every view uses:

swift/Theme/Theme.swift
import SwiftUI
import UIKit

struct RGBA: Sendable {
  let red: CGFloat
  let green: CGFloat
  let blue: CGFloat
  let alpha: CGFloat

  init(_ hex: UInt32, _ alpha: CGFloat = 1) {
    red = CGFloat((hex >> 16) & 0xFF) / 255
    green = CGFloat((hex >> 8) & 0xFF) / 255
    blue = CGFloat(hex & 0xFF) / 255
    self.alpha = alpha
  }
swift/Theme/Tokens.swift
import SwiftUI

public enum EsuissColor {
  public static let background = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x000000))
  public static let foreground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7))
  public static let card = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x1D1D1F))
  public static let cardForeground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7))
  public static let popover = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x1D1D1F))
  public static let popoverForeground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7))
  public static let primary = Color.adaptive(light: .init(0x0071E3), dark: .init(0x2997FF))
  public static let primaryForeground = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0xFFFFFF))
  public static let secondary = Color.adaptive(light: .init(0x000000, 0.04), dark: .init(0xFFFFFF, 0.1))
  public static let secondaryForeground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7))
  public static let muted = Color.adaptive(light: .init(0xF5F5F7), dark: .init(0x161617))
  public static let mutedForeground = Color.adaptive(light: .init(0x6E6E73), dark: .init(0xA1A1A6))
  public static let accent = Color.adaptive(light: .init(0x000000, 0.06), dark: .init(0xFFFFFF, 0.1))
  1. Copy the source and every file it uses:
swift/Components/Label.swift
import SwiftUI

public struct FieldLabel<Content: View>: View {
  let content: Content

  @Environment(\.fieldInset) private var inset
  @Environment(\.isEnabled) private var isEnabled

  public init(@ViewBuilder content: () -> Content) {
    self.content = content()
  }

  public var body: some View {
    content
      .scaledTextStyle(.base, weight: inset ? .regular : .semibold)
      .foregroundStyle(EsuissColor.foreground)
swift/Components/Field.swift
import SwiftUI

extension EnvironmentValues {
  @Entry public var isInvalid = false
  @Entry var fieldInset = false
  @Entry var fieldGroupInset = false
  @Entry var fieldSetInset = false
}

extension View {
  public func invalid(_ isInvalid: Bool = true) -> some View {
    environment(\.isInvalid, isInvalid)
  }
}

public enum FieldOrientation: CaseIterable, Sendable {
swift/Components/Separator.swift
import SwiftUI

public struct Separator: View {
  let orientation: Axis

  public init(_ orientation: Axis = .horizontal) {
    self.orientation = orientation
  }

  public var body: some View {
    Rectangle()
      .fill(EsuissColor.border)
      .frame(width: orientation == .vertical ? 1 : nil, height: orientation == .horizontal ? 1 : nil)
      .frame(maxWidth: orientation == .horizontal ? .infinity : nil, maxHeight: orientation == .vertical ? .infinity : nil)
      .accessibilityHidden(true)
  }
swift/Components/TextVariant.swift
import SwiftUI

public enum TextVariant: CaseIterable, Sendable {
  case `default`, h1, h2, h3, h4, p, blockquote, code, lead, large, small, muted

  var style: TextStyle {
    switch self {
    case .h1: .xl3
    case .h2: .xl2
    case .h3: .xl
    case .h4, .lead, .large: .lg
    case .code, .small, .muted: .sm
    default: .base
    }
  }
  1. Add the files to your app target.
  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/Label.kt
package com.esuiss.ui.components

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.esuiss.ui.theme.EsuissTheme
import com.esuiss.ui.theme.Typography
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 { Label } from "@/components/ui/label"
<Label>Your email address</Label>

Set a nativeID on the label and point the control's accessibilityLabelledBy at it, so screen readers read the label with the control.

For a checkbox or switch, pass an onPress that toggles the control, which gives a larger touch target. Inside a Field, use FieldLabel instead.

Examples

With input

A label linked to an input through nativeID and accessibilityLabelledBy.

iOS
Label (with input) on iOS in light modeLabel (with input) on iOS in dark mode
import { View } from "react-native"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"

export function LabelWithInputExample() {
  return (
    <View className="gap-2">
      <Label nativeID="label-email">Email</Label>
      <Input
        accessibilityLabelledBy="label-email"
        placeholder="[email protected]"
      />
    </View>
  )
}
import EsuissUI
import SwiftUI

struct LabelWithInput: View {
  @State private var email = ""

  var body: some View {
    VStack(alignment: .leading, spacing: 8) {
      FieldLabel("Email")
        .accessibilityHidden(true)
      TextField(String("[email protected]"), text: $email)
        .textFieldStyle(.input)
        .keyboardType(.emailAddress)
        .textInputAutocapitalization(.never)
        .accessibilityLabel(Text("Email"))
    }
    .frame(maxWidth: .infinity, alignment: .leading)
  }
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.Input
import com.esuiss.ui.components.Label

@Composable
fun LabelWithInput() {
    var email by rememberSaveable { mutableStateOf("") }
    Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
        Label("Email")
        Input(email, { email = it }, Modifier.semantics { contentDescription = "Email" }, placeholder = "[email protected]")
    }
}

With checkbox

A label next to a checkbox that toggles it when pressed.

iOS
Label (with checkbox) on iOS in light modeLabel (with checkbox) on iOS in dark mode
import * as React from "react"
import { View } from "react-native"
import { Checkbox } from "@/components/ui/checkbox"
import { Label } from "@/components/ui/label"

function WithCheckboxDemo() {
  const [checked, setChecked] = React.useState(false)
  return (
    <View className="flex-row items-center gap-3">
      <Checkbox
        accessibilityLabelledBy="label-terms"
        checked={checked}
        onCheckedChange={setChecked}
      />
      <Label nativeID="label-terms" onPress={() => setChecked((v) => !v)}>
        Accept terms and conditions
      </Label>
    </View>
  )
}

export function LabelWithCheckboxExample() {
  return (
    <WithCheckboxDemo />
  )
}
import EsuissUI
import SwiftUI

struct LabelWithCheckbox: View {
  @State private var checked = false

  var body: some View {
    Toggle(isOn: $checked) {
      FieldLabel("Accept terms and conditions")
    }
    .toggleStyle(.checkbox)
  }
}
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.Checkbox
import com.esuiss.ui.components.Label

@Composable
fun LabelWithCheckbox() {
    var checked by rememberSaveable { mutableStateOf(false) }
    Row(horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically) {
        Checkbox(checked, { checked = it }, Modifier.semantics { contentDescription = "Accept terms and conditions" })
        Label(
            "Accept terms and conditions",
            Modifier.clickable(interactionSource = remember { MutableInteractionSource() }, indication = null) { checked = !checked },
        )
    }
}

Disabled

A disabled label.

iOS
Label (disabled) on iOS in light modeLabel (disabled) on iOS in dark mode
import { Label } from "@/components/ui/label"

export function LabelDisabledExample() {
  return (
    <Label disabled>Disabled label</Label>
  )
}
import EsuissUI
import SwiftUI

struct LabelDisabledExample: View {
  var body: some View {
    FieldLabel("Disabled label")
      .disabled(true)
  }
}
import androidx.compose.runtime.Composable
import com.esuiss.ui.components.Label

@Composable
fun LabelDisabledExample() {
    Label("Disabled label", enabled = false)
}

API Reference

01

Label

Also accepts the props of React.ComponentProps<typeof LabelPrimitive.Text>.

Accessibility

  • The label text reports its disabled state in accessibilityState.
  • Link it to its control with nativeID on the label and accessibilityLabelledBy on the control.

Notes

  • Text uses the text-foreground token, and opacity-50 when disabled.
  • Light and dark colors follow the system appearance.
NextMarkerPreviousKeypad

On this page

  1. 01Installation
    • CLI
    • Manual
  2. 02Usage
  3. 03Examples
    • With input
    • With checkbox
    • Disabled
  4. 04API Reference
    • Label
  5. 05Accessibility
  6. 06Notes
  7. 07Installation
    • Manual
  8. 08Usage
  9. 09Examples
    • With input
    • With checkbox
    • Disabled
  10. 10API Reference
    • Label
  11. 11Accessibility
  12. 12Notes