Skip to main content
ionic-design Guide to using Ionic Framework components for beautiful native-looking Capacitor apps. Covers component usage, theming, platform-specific styling, and best practices for mobile UI. Use this skill when users need help with Ionic components or mobile UI design.
Jump to install Skills Marketplace Discover and explore AI skills built by the community.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Copy promptShow prompt details A direct command skips the review prompt. Inspect the source before running it.
npx skills add https://github.com/Cap-go/capgo-skills --skill ionic-designThe command stays on one line. Scroll horizontally to inspect it before copying.
Prefer a local copy? Download the files currently available to SkillsMP.
Download Zip Downloading... More from this repository
Related occupations SOC
Based on SOC occupation classification
name ionic-design description Guide to using Ionic Framework components for beautiful native-looking Capacitor apps. Covers component usage, theming, platform-specific styling, and best practices for mobile UI. Use this skill when users need help with Ionic components or mobile UI design.
Ionic Framework Design Guide
Build beautiful, native-looking mobile apps with Ionic Framework and Capacitor.
When to Use This Skill
User is using Ionic components
User wants native-looking UI
User asks about Ionic theming
User needs mobile UI patterns
User wants platform-specific styling
What is Ionic Framework?
Ionic provides:
100+ mobile-optimized UI components
Automatic iOS/Android platform styling
Built-in dark mode support
Accessibility out of the box
Works with React, Vue, Angular, or vanilla JS
Getting Started
Installation
npx create-vite@latest my-app --template react-ts
cd my-app
npm install @ionic/react @ionic/react-router
npx create-vite@latest my-app --template vue-ts
cd my-app
npm install @ionic/vue @ionic/vue-router
npm install @capacitor/core @capacitor/cli
npx cap init
Setup (React)
import React from 'react' ;
import { createRoot } from ;
{ , setupIonicReact } ;
;
;
;
;
;
;
;
;
;
;
;
;
();
root = ( . ( )!);
root. (
);
'react-dom/client'
import
IonApp
from
'@ionic/react'
import
App
from
'./App'
import
'@ionic/react/css/core.css'
import
'@ionic/react/css/normalize.css'
import
'@ionic/react/css/structure.css'
import
'@ionic/react/css/typography.css'
import
'@ionic/react/css/padding.css'
import
'@ionic/react/css/float-elements.css'
import
'@ionic/react/css/text-alignment.css'
import
'@ionic/react/css/text-transformation.css'
import
'@ionic/react/css/flex-utils.css'
import
'@ionic/react/css/display.css'
import
'./theme/variables.css'
setupIonicReact
const
createRoot
document
getElementById
'root'
render
<IonApp >
<App />
</IonApp >
Core Components
Page Structure import {
IonPage ,
IonHeader ,
IonToolbar ,
IonTitle ,
IonContent ,
IonButtons ,
IonBackButton ,
} from '@ionic/react' ;
function MyPage ( ) {
return (
<IonPage >
<IonHeader >
<IonToolbar >
<IonButtons slot ="start" >
<IonBackButton defaultHref ="/home" />
</IonButtons >
<IonTitle > Page Title</IonTitle >
</IonToolbar >
</IonHeader >
<IonContent fullscreen >
{/* Large title for iOS */}
<IonHeader collapse ="condense" >
<IonToolbar >
<IonTitle size ="large" > Page Title</IonTitle >
</IonToolbar >
</IonHeader >
{/* Page content */}
<div className ="ion-padding" >
Your content here
</div >
</IonContent >
</IonPage >
);
}
Lists import {
IonList ,
IonItem ,
IonLabel ,
IonNote ,
IonAvatar ,
IonIcon ,
IonItemSliding ,
IonItemOptions ,
IonItemOption ,
} from '@ionic/react' ;
import { chevronForward, trash, archive } from 'ionicons/icons' ;
function ContactList ( ) {
return (
<IonList >
{/* Simple item */}
<IonItem >
<IonLabel > Simple Item</IonLabel >
</IonItem >
{/* Item with detail */}
<IonItem detail button >
<IonLabel >
<h2 > Item Title</h2 >
<p > Item description text</p >
</IonLabel >
<IonNote slot ="end" > Note</IonNote >
</IonItem >
{/* Item with avatar */}
<IonItem >
<IonAvatar slot ="start" >
<img src ="/avatar.jpg" alt ="" />
</IonAvatar >
<IonLabel >
<h2 > John Doe</h2 >
<p > john@example.com</p >
</IonLabel >
</IonItem >
{/* Sliding item */}
<IonItemSliding >
<IonItem >
<IonLabel > Swipe me</IonLabel >
</IonItem >
<IonItemOptions side ="end" >
<IonItemOption color ="danger" >
<IonIcon slot ="icon-only" icon ={trash} />
</IonItemOption >
<IonItemOption >
<IonIcon slot ="icon-only" icon ={archive} />
</IonItemOption >
</IonItemOptions >
</IonItemSliding >
</IonList >
);
}
Forms import {
IonInput ,
IonTextarea ,
IonSelect ,
IonSelectOption ,
IonToggle ,
IonCheckbox ,
IonRadioGroup ,
IonRadio ,
IonItem ,
IonLabel ,
IonButton ,
} from '@ionic/react' ;
function MyForm ( ) {
return (
<form >
{/* Text input */}
<IonItem >
<IonInput
label ="Email"
labelPlacement ="floating"
type ="email"
placeholder ="Enter email"
/>
</IonItem >
{/* Password */}
<IonItem >
<IonInput
label ="Password"
labelPlacement ="floating"
type ="password"
/>
</IonItem >
{/* Textarea */}
<IonItem >
<IonTextarea
label ="Bio"
labelPlacement ="floating"
rows ={4}
placeholder ="Tell us about yourself"
/>
</IonItem >
{/* Select */}
<IonItem >
<IonSelect label ="Country" placeholder ="Select" >
<IonSelectOption value ="us" > United States</IonSelectOption >
<IonSelectOption value ="uk" > United Kingdom</IonSelectOption >
<IonSelectOption value ="de" > Germany</IonSelectOption >
</IonSelect >
</IonItem >
{/* Toggle */}
<IonItem >
<IonToggle > Enable notifications</IonToggle >
</IonItem >
{/* Checkbox */}
<IonItem >
<IonCheckbox slot ="start" />
<IonLabel > I agree to terms</IonLabel >
</IonItem >
{/* Radio group */}
<IonRadioGroup >
<IonItem >
<IonRadio value ="small" > Small</IonRadio >
</IonItem >
<IonItem >
<IonRadio value ="medium" > Medium</IonRadio >
</IonItem >
<IonItem >
<IonRadio value ="large" > Large</IonRadio >
</IonItem >
</IonRadioGroup >
<IonButton expand ="block" type ="submit" >
Submit
</IonButton >
</form >
);
}
Buttons import { IonButton , IonIcon } from '@ionic/react' ;
import { heart, share, download } from 'ionicons/icons' ;
function Buttons ( ) {
return (
<>
{/* Fill variants */}
<IonButton > Solid</IonButton >
<IonButton fill ="outline" > Outline</IonButton >
<IonButton fill ="clear" > Clear</IonButton >
{/* Colors */}
<IonButton color ="primary" > Primary</IonButton >
<IonButton color ="secondary" > Secondary</IonButton >
<IonButton color ="danger" > Danger</IonButton >
<IonButton color ="success" > Success</IonButton >
{/* Sizes */}
<IonButton size ="small" > Small</IonButton >
<IonButton size ="default" > Default</IonButton >
<IonButton size ="large" > Large</IonButton >
{/* With icons */}
<IonButton >
<IonIcon slot ="start" icon ={heart} />
Like
</IonButton >
{/* Icon only */}
<IonButton >
<IonIcon slot ="icon-only" icon ={share} />
</IonButton >
{/* Full width */}
<IonButton expand ="block" > Block Button</IonButton >
<IonButton expand ="full" > Full Width</IonButton >
</>
);
}
Cards import {
IonCard ,
IonCardHeader ,
IonCardTitle ,
IonCardSubtitle ,
IonCardContent ,
IonImg ,
IonButton ,
} from '@ionic/react' ;
function Cards ( ) {
return (
<IonCard >
<IonImg src ="/card-image.jpg" alt ="" />
<IonCardHeader >
<IonCardSubtitle > Card Subtitle</IonCardSubtitle >
<IonCardTitle > Card Title</IonCardTitle >
</IonCardHeader >
<IonCardContent >
Card content goes here. This is a standard card with
an image, title, subtitle, and content.
</IonCardContent >
<div className ="ion-padding-horizontal ion-padding-bottom" >
<IonButton fill ="clear" > Action 1</IonButton >
<IonButton fill ="clear" > Action 2</IonButton >
</div >
</IonCard >
);
}
Modals and Sheets import { IonModal , IonButton , IonContent , IonHeader , IonToolbar , IonTitle } from '@ionic/react' ;
import { useState, useRef } from 'react' ;
function ModalExample ( ) {
const [isOpen, setIsOpen] = useState (false );
const modal = useRef<HTMLIonModalElement >(null );
return (
<>
<IonButton onClick ={() => setIsOpen(true)}>Open Modal</IonButton >
{/* Full page modal */}
<IonModal isOpen ={isOpen} onDidDismiss ={() => setIsOpen(false)}>
<IonHeader >
<IonToolbar >
<IonTitle > Modal Title</IonTitle >
<IonButton slot ="end" onClick ={() => setIsOpen(false)}>
Close
</IonButton >
</IonToolbar >
</IonHeader >
<IonContent >
<p > Modal content</p >
</IonContent >
</IonModal >
{/* Bottom sheet */}
<IonModal
ref ={modal}
trigger ="open-sheet"
initialBreakpoint ={0.5}
breakpoints ={[0, 0.25 , 0.5 , 0.75 , 1 ]}
>
<IonContent >
<div className ="ion-padding" >
<h2 > Sheet Content</h2 >
<p > Drag to resize</p >
</div >
</IonContent >
</IonModal >
<IonButton id ="open-sheet" > Open Sheet</IonButton >
</>
);
}
Navigation
Tab Navigation import {
IonTabs ,
IonTabBar ,
IonTabButton ,
IonIcon ,
IonLabel ,
IonRouterOutlet ,
} from '@ionic/react' ;
import { Route , Redirect } from 'react-router-dom' ;
import { home, search, person } from 'ionicons/icons' ;
function TabsLayout ( ) {
return (
<IonTabs >
<IonRouterOutlet >
<Route exact path ="/tabs/home" component ={HomePage} />
<Route exact path ="/tabs/search" component ={SearchPage} />
<Route exact path ="/tabs/profile" component ={ProfilePage} />
<Route exact path ="/tabs" >
<Redirect to ="/tabs/home" />
</Route >
</IonRouterOutlet >
<IonTabBar slot ="bottom" >
<IonTabButton tab ="home" href ="/tabs/home" >
<IonIcon icon ={home} />
<IonLabel > Home</IonLabel >
</IonTabButton >
<IonTabButton tab ="search" href ="/tabs/search" >
<IonIcon icon ={search} />
<IonLabel > Search</IonLabel >
</IonTabButton >
<IonTabButton tab ="profile" href ="/tabs/profile" >
<IonIcon icon ={person} />
<IonLabel > Profile</IonLabel >
</IonTabButton >
</IonTabBar >
</IonTabs >
);
}
Stack Navigation import { IonReactRouter } from '@ionic/react-router' ;
import { IonRouterOutlet } from '@ionic/react' ;
import { Route } from 'react-router-dom' ;
function App ( ) {
return (
<IonReactRouter >
<IonRouterOutlet >
<Route exact path ="/" component ={Home} />
<Route exact path ="/detail/:id" component ={Detail} />
</IonRouterOutlet >
</IonReactRouter >
);
}
Theming
Theme Variables
:root {
--ion-color-primary : #3880ff ;
--ion-color-primary-rgb : 56 , 128 , 255 ;
--ion-color-primary-contrast : #ffffff ;
--ion-color-primary-shade : #3171e0 ;
--ion-color-primary-tint : #4c8dff ;
--ion-color-secondary : #3dc2ff ;
--ion-color-brand : #ff6b35 ;
--ion-color-brand-rgb : 255 , 107 , 53 ;
--ion-color-brand-contrast : #ffffff ;
--ion-color-brand-shade : #e05e2f ;
--ion-color-brand-tint : #ff7a49 ;
}
@media (prefers-color-scheme : dark) {
:root {
--ion-background-color : #121212 ;
--ion-text-color : #ffffff ;
--ion-color-step-50 : #1e1e1e ;
--ion-color-step-100 : #2a2a2a ;
}
}
.ios {
--ion-toolbar-background : #f8f8f8 ;
}
.md {
--ion-toolbar-background : #ffffff ;
}
Custom Component Styling
ion-content {
--background : var (--ion-background-color);
}
ion-card {
--background : #ffffff ;
border-radius : 16px ;
box-shadow : 0 4px 16px rgba (0 , 0 , 0 , 0.1 );
}
.ios ion-toolbar {
--border-width : 0 ;
}
.md ion-toolbar {
--border-width : 0 0 1px 0 ;
}
Platform-Specific Code
Detect Platform import { isPlatform } from '@ionic/react' ;
if (isPlatform ('ios' )) {
}
if (isPlatform ('android' )) {
}
if (isPlatform ('hybrid' )) {
}
if (isPlatform ('mobileweb' )) {
}
Conditional Rendering import { isPlatform, IonIcon } from '@ionic/react' ;
import { chevronBack, arrowBack } from 'ionicons/icons' ;
function BackButton ( ) {
return (
<IonIcon
icon ={isPlatform( 'ios ') ? chevronBack : arrowBack }
/>
);
}
Best Practices
Performance
import { IonVirtualScroll } from '@ionic/react' ;
<IonVirtualScroll
items ={items}
renderItem ={(item) => (
<IonItem key ={item.id} >
<IonLabel > {item.name}</IonLabel >
</IonItem >
)}
/>
<IonImg src ={url} />
Accessibility
<IonButton aria-label="Delete item" >
<IonIcon slot ="icon-only" icon ={trash} />
</IonButton >
<IonItem button role ="link" >
<IonLabel > Clickable item</IonLabel >
</IonItem >
Safe Area
<IonContent >
{}
</IonContent >
<div style ={{ paddingTop: 'env (safe-area-inset-top )' }}>
Custom header
</div >
Resources