SwiftUI makes it easy to build UI — but building reusable components that look consistent across your entire app is a different challenge.
As apps grow, UI duplication becomes a real problem:
- repeated button styles
- inconsistent card shapes
- duplicated text modifiers
- copy-paste shadows
- multiple versions of the same layout
A component library solves all of this.
Today you’ll learn how to build a modern, scalable, Apple-style SwiftUI Component Library that includes:
- buttons
- cards
- text fields
- floating panels
- chips
- design tokens (colors, radii, shadows)
- reusable modifiers
- consistent styling
This is the exact structure I use in real production apps.
Let’s build it. 🚀
🧱 1. Start With a Design Folder
Design/
├── Colors.swift
├── Radii.swift
├── Shadows.swift
└── Typography.swift
This gives your app consistent design tokens.
Colors.swift
enum AppColor {
static let primary = Color.blue
static let background = Color(.systemBackground)
static let glassStroke = Color.white.opacity(0.25)
}
Radii.swift
enum AppRadius {
static let small: CGFloat = 10
static let medium: CGFloat = 16
static let large: CGFloat = 22
}
Shadows.swift
enum AppShadow {
static let card = Color.black.opacity(0.18)
static let glow = Color.blue.opacity(0.3)
}
Typography.swift
enum AppFont {
static let title = Font.system(.title3, design: .rounded).bold()
static let body = Font.system(.body, design: .rounded)
}
One place → full app consistency.
🔵 2. Reusable Button Styles
A clean button style gives your UI an identity.
PrimaryButton
struct PrimaryButton: View {
let title: String
let action: () -> Void
var body: some View {
Button(action: action) {
Text(title)
.font(AppFont.body.bold())
.padding(.horizontal, 28)
.padding(.vertical, 14)
.background(AppColor.primary)
.foregroundColor(.white)
.clipShape(RoundedRectangle(cornerRadius: AppRadius.medium, style: .continuous))
.shadow(color: AppShadow.card, radius: 16, y: 8)
}
}
}
Usage:
PrimaryButton(title: "Continue") {
print("Pressed")
}
🟦 3. Glass Card Component (Reusable)
struct GlassCard<Content: View>: View {
@ViewBuilder let content: () -> Content
var body: some View {
content()
.padding(20)
.background(.ultraThinMaterial)
.clipShape(RoundedRectangle(cornerRadius: AppRadius.large, style: .continuous))
.overlay(
RoundedRectangle(cornerRadius: AppRadius.large, style: .continuous)
.stroke(AppColor.glassStroke, lineWidth: 1)
)
.shadow(color: AppShadow.card, radius: 24, y: 12)
}
}
Usage:
GlassCard {
VStack(alignment: .leading) {
Text("Glass Card")
.font(AppFont.title)
Text("Reusable glassmorphic component.")
.foregroundColor(.secondary)
}
}
🟩 4. TextField Component (Modern, Clean)
struct AppTextField: View {
var title: String
@Binding var text: String
var body: some View {
TextField(title, text: $text)
.padding(.horizontal, 14)
.padding(.vertical, 12)
.background(.ultraThinMaterial)
.clipShape(RoundedRectangle(cornerRadius: AppRadius.medium))
.overlay(
RoundedRectangle(cornerRadius: AppRadius.medium)
.stroke(AppColor.glassStroke)
)
.shadow(color: AppShadow.card.opacity(0.25), radius: 12, y: 6)
}
}
Usage:
@State private var name = ""
AppTextField(title: "Name", text: $name)
🔶 5. Chips / Tags
struct Chip: View {
let label: String
let icon: String?
var body: some View {
HStack(spacing: 6) {
if let icon { Image(systemName: icon) }
Text(label)
}
.font(.caption)
.padding(.horizontal, 12)
.padding(.vertical, 8)
.background(.ultraThinMaterial)
.clipShape(Capsule())
.overlay(Capsule().stroke(AppColor.glassStroke))
}
}
Example:
HStack {
Chip(label: "SwiftUI", icon: "swift")
Chip(label: "Design", icon: "paintbrush")
}
🪟 6. Floating Panel (Reusable Bottom Sheet Top Section)
struct FloatingPanel<Content: View>: View {
@ViewBuilder let content: () -> Content
var body: some View {
VStack(spacing: 0) {
Capsule()
.fill(AppColor.glassStroke)
.frame(width: 40, height: 6)
.padding(.top, 10)
content()
.padding()
}
.background(.regularMaterial)
.clipShape(RoundedRectangle(cornerRadius: AppRadius.large, style: .continuous))
.shadow(color: .black.opacity(0.25), radius: 30, y: 14)
}
}
🧩 7. Reusable Modifiers (Powerful Tool!)
A clean reusable modifier:
struct CardPadding: ViewModifier {
func body(content: Content) -> some View {
content
.padding(20)
.clipShape(RoundedRectangle(cornerRadius: AppRadius.large))
}
}
extension View {
func cardPadding() -> some View {
modifier(CardPadding())
}
}
Modifiers keep your views clean and declarative.
🗂 8. Suggested Folder Structure
Design/
│ Colors.swift
│ Radii.swift
│ Shadows.swift
│ Typography.swift
│
Components/
│ Buttons/
│ Cards/
│ TextFields/
│ Panels/
│ Chips/
│ Modifiers/
Everything clean. Everything scalable.
🚀 Final Thoughts
A SwiftUI Component Library gives you:
- consistent design
- faster iteration
- reusable building blocks
- scalable architecture
- easier team onboarding
- more polished UI
This is how you go from “building screens” → to building systems.
Top comments (0)