How to Design Good Mobile UIs Without Being a Designer
Most developers aren't designers. But most developers still have to design UIs.
You don't need a design degree to build interfaces that don't make users frustrated. You need a few simple heuristics applied consistently.
This isn't about making beautiful, award-winning designs. It's about making UIs that feel clean, understandable, and not broken. That's achievable without a designer, if you follow a handful of rules.
The Core Principle: Consistency Over Creativity
Good design for non-designers isn't about being original. It's about being predictable and consistent.
Users shouldn't have to think about how your app works. Buttons should look like buttons. Headings should look like headings. Spacing should feel intentional, not random.
When in doubt, pick the boring choice. Standard navigation patterns, familiar layouts, conventional colors. Save creativity for product features, not UI experiments.
Spacing: The 8pt Grid System
Bad spacing is the easiest way to make an app look unprofessional. Random margins, inconsistent padding, things that almost line up but don't.
The fix: use multiples of 8 for all spacing.
The 8pt Grid
Every margin, padding, gap, and offset should be a multiple of 8 pixels:
- 8px, 16px, 24px, 32px, 40px, etc.
This creates visual rhythm. Elements feel aligned even if they're not directly touching.
In Practice
Bad:
<View style={{ padding: 12, marginBottom: 15 }}>
Good:
<View style={{ padding: 16, marginBottom: 16 }}>
Why 8?
- Divisible by 2, making it easy to create half-spacing (4px for tight elements)
- Scales well across different screen densities
- Matches the default grid most design tools use (Figma, Sketch)
Apply It Everywhere
- Padding inside cards: 16px or 24px
- Margins between sections: 24px or 32px
- Gaps between list items: 8px or 16px
- Button padding: 12px vertical, 24px horizontal (exception for touch targets)
The actual values matter less than consistency. Pick a scale and stick to it.
Typography: A Simple, Reliable Scale
Font sizes shouldn't be random guesses. Use a defined scale and apply it consistently.
The Basic Scale
- 12px - Captions, secondary info, timestamps
- 14px - Body text, most content
- 16px - Emphasized body text, button labels
- 20px - Subheadings
- 24px - Section headings
- 32px - Page titles
Font Weights
- Regular (400) - Body text
- Semi-bold (600) - Emphasized text, labels
- Bold (700) - Headings, buttons
Don't use more than 2-3 weights in your app. Too many weights make text feel inconsistent.
Line Height
Set line height to 1.4-1.6 times the font size for readability:
- 14px font → 20px line height
- 16px font → 24px line height
Platform Defaults
iOS and Android have different typographic conventions:
- iOS uses San Francisco with subtle weight variations for hierarchy
- Android uses Roboto with more contrasting sizes for hierarchy
For cross-platform consistency, you can use a custom font (like Inter or Roboto), or embrace platform defaults by setting different styles per platform using Platform.select.
In Code
export const typography = {
sizes: {
caption: 12,
body: 14,
bodyLarge: 16,
subhead: 20,
heading: 24,
title: 32,
},
weights: {
regular: '400',
semibold: '600',
bold: '700',
},
lineHeights: {
caption: 16,
body: 20,
bodyLarge: 24,
subhead: 28,
heading: 32,
title: 40,
},
};
Reference this object instead of hardcoding sizes everywhere.
Color: Keep It Simple
Developers tend to either use too many colors or not enough contrast.
The Minimal Palette
You need:
- Primary color - Buttons, links, active states (1 color)
- Background colors - Page background, card background (2 shades)
- Text colors - Primary text, secondary text, disabled text (3 shades of gray)
- Error color - Alerts, validation messages (1 color, usually red)
- Success color - Confirmations (1 color, usually green)
That's it. 8-9 colors total.
Contrast
Text must be readable. Use a contrast checker to ensure text meets WCAG AA standards:
- Body text needs 4.5:1 contrast ratio
- Large text (18px+) needs 3:1
Common mistakes:
- Light gray text on white background (unreadable)
- Low contrast for disabled states (confusing)
- Not enough contrast for links
Semantic Colors
Don't use "blue" or "red" directly. Use semantic names:
export const colors = {
primary: '#007AFF',
background: '#FFFFFF',
surface: '#F5F5F5',
text: '#000000',
textSecondary: '#666666',
textDisabled: '#AAAAAA',
error: '#FF3B30',
success: '#34C759',
border: '#E0E0E0',
};
Now if you want to change your primary color, you change one value, not 50.
Visual Hierarchy: Make Important Things Obvious
Users should immediately understand what's important on a screen.
How to Create Hierarchy
Size. Bigger = more important. Headings should be noticeably larger than body text.
Weight. Bolder = more important. Use bold for labels, regular for values.
Color. High contrast = more important. Primary actions use your primary color, secondary actions use gray.
Spacing. More space around an element = more important. Key actions should have breathing room.
Example
Bad (everything looks equally important):
<View>
<Text style={{ fontSize: 16, color: 'black' }}>Welcome</Text>
<Text style={{ fontSize: 14, color: 'black' }}>Check out these new features</Text>
<Button title="Learn More" />
<Button title="Dismiss" />
</View>
Good (clear hierarchy):
<View>
<Text style={{ fontSize: 24, fontWeight: 'bold', color: colors.text }}>
Welcome
</Text>
<Text style={{ fontSize: 14, color: colors.textSecondary, marginTop: 8 }}>
Check out these new features
</Text>
<View style={{ marginTop: 24, gap: 12 }}>
<Button title="Learn More" color={colors.primary} />
<Button title="Dismiss" color={colors.textSecondary} />
</View>
</View>
The heading is larger and bold. The description is smaller and gray. The primary button uses the primary color. The secondary button is muted. Spacing separates sections.
Touch Targets: Make Buttons Tappable
On mobile, buttons need to be big enough to tap comfortably.
The 44px Rule
Minimum touch target size: 44x44 pixels (Apple's guideline, also works for Android).
If your button text is small, add padding to make the touch area larger:
<Pressable
style={{
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 8,
backgroundColor: colors.primary,
}}
>
<Text style={{ color: 'white', fontSize: 16 }}>Submit</Text>
</Pressable>
This creates a touch area much larger than the text itself.
Common Mistakes
Tiny buttons. Users miss them or tap the wrong thing.
Buttons too close together. Users accidentally tap the wrong button.
No visual feedback. Users don't know if their tap registered.
Fix: Add Press States
<Pressable
style={({ pressed }) => ({
backgroundColor: pressed ? '#005BB5' : colors.primary,
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 8,
})}
>
<Text style={{ color: 'white' }}>Submit</Text>
</Pressable>
The button changes color when pressed, giving immediate feedback.
Alignment: Pick a Side and Stick to It
Misaligned elements look sloppy.
Left-Align Text (for LTR Languages)
Body text, labels, and paragraphs should left-align by default. Centered text is harder to read for more than a few words.
Use center alignment sparingly:
- Empty states
- Modals
- Single-line headings
Align Elements to a Grid
All elements should align to consistent vertical lines.
Example:
<View style={{ padding: 16 }}>
<Text>Name</Text>
<TextInput style={{ marginTop: 8 }} />
<Text style={{ marginTop: 16 }}>Email</Text>
<TextInput style={{ marginTop: 8 }} />
</View>
Labels and inputs align to the left edge. Consistent margin creates rhythm.
Common Mobile UI Mistakes
1. Too Much on One Screen
Mobile screens are small. Don't cram everything into one view.
Fix: Break complex screens into steps or tabs.
2. Ignoring Platform Conventions
iOS users expect navigation at the bottom. Android users expect navigation at the top (or bottom for Material Design 3).
Fix: Use platform-specific navigation patterns or a cross-platform library like React Navigation that handles this.
3. Not Designing for Different Screen Sizes
Your app should work on small Android phones and large iPads.
Fix: Use flexible layouts (flex: 1, percentages) instead of hardcoded widths.
4. Poor Form UX
Forms with no labels, unclear error messages, or tiny input fields frustrate users.
Fix:
- Label every input clearly
- Show error messages inline
- Use appropriate keyboard types (
email,numeric) - Make inputs large enough to tap easily
5. No Loading or Empty States
Users see blank screens while data loads or when there's no content.
Fix: Add loading spinners and empty state messages ("No messages yet").
Use a Component Library
If design isn't your strength, use a pre-built component library.
React Native Paper
React Native Paper implements Material Design. Buttons, cards, inputs, modals—all styled consistently out of the box.
NativeBase
NativeBase provides cross-platform components with built-in theming.
GlueStack UI
GlueStack UI offers customizable components with design tokens for spacing and colors.
Why this helps: These libraries handle spacing, typography, and interaction states for you. You focus on layout and content, not styling every button from scratch.
Real Example: Before and After
Before (No Design Principles)
<View style={{ padding: 10 }}>
<Text style={{ fontSize: 18 }}>Profile</Text>
<Text style={{ fontSize: 12, marginTop: 5 }}>Update your information</Text>
<TextInput placeholder="Name" style={{ marginTop: 10, padding: 5 }} />
<TextInput placeholder="Email" style={{ marginTop: 8, padding: 5 }} />
<Button title="Save" />
</View>
Issues:
- Random padding values (10, 5)
- Random margins (5, 8, 10)
- Font sizes don't follow a scale
- No visual hierarchy
- Inputs too small to tap easily
After (Design Principles Applied)
import { typography, colors, spacing } from '../theme';
<View style={{ padding: spacing.lg }}>
<Text style={{
fontSize: typography.sizes.heading,
fontWeight: typography.weights.bold,
color: colors.text,
}}>
Profile
</Text>
<Text style={{
fontSize: typography.sizes.body,
color: colors.textSecondary,
marginTop: spacing.sm,
}}>
Update your information
</Text>
<TextInput
placeholder="Name"
style={{
marginTop: spacing.lg,
padding: spacing.md,
backgroundColor: colors.surface,
borderRadius: 8,
fontSize: typography.sizes.body,
}}
/>
<TextInput
placeholder="Email"
style={{
marginTop: spacing.md,
padding: spacing.md,
backgroundColor: colors.surface,
borderRadius: 8,
fontSize: typography.sizes.body,
}}
/>
<Pressable
style={{
marginTop: spacing.lg,
paddingVertical: spacing.md,
paddingHorizontal: spacing.lg,
backgroundColor: colors.primary,
borderRadius: 8,
alignItems: 'center',
}}
>
<Text style={{
color: 'white',
fontSize: typography.sizes.bodyLarge,
fontWeight: typography.weights.semibold,
}}>
Save
</Text>
</Pressable>
</View>
Improvements:
- Consistent spacing (8pt grid)
- Typography scale applied
- Clear visual hierarchy (heading bold, description gray)
- Large touch targets
- Design tokens for easy theming
Quick Checklist
Use this checklist to catch common design issues:
- [ ] All spacing uses multiples of 8 (or a defined scale)
- [ ] All font sizes come from a defined typography scale
- [ ] Color palette is consistent and limited
- [ ] Text has sufficient contrast (4.5:1 for body, 3:1 for large)
- [ ] All touch targets are at least 44x44 pixels
- [ ] Buttons have visual feedback on press
- [ ] There's clear visual hierarchy (important things stand out)
- [ ] Text is left-aligned (except for special cases)
- [ ] Elements align to a consistent grid
- [ ] Loading and empty states are handled
Conclusion
You don't need to be a designer to build UIs that don't frustrate users.
Follow these rules:
- Use an 8pt grid for spacing
- Use a defined typography scale
- Keep your color palette small and consistent
- Create clear visual hierarchy
- Make touch targets large enough
- Align everything to a grid
Consistency beats creativity. Boring beats broken.
If you apply these principles, your app will feel intentional and professional—even if no designer touched it.
Sources: