Typography Is 90% of Your UI
Most of what users see on screen is text. Think about it. Headings, paragraphs, labels, buttons, navigation items, error messages, tooltips, table cells. It's all text. If your typography is off, everything feels wrong even if you can't quite explain why. A website can have beautiful colors, perfect spacing, and stunning illustrations, but if the text is hard to read or the hierarchy is unclear, the whole thing falls apart.
Good typography makes content easy to read, creates clear visual hierarchy, and makes your app feel professional. The best part? You don't need to be a designer to get typography right. You just need to understand a handful of principles and apply them consistently. This guide covers everything a frontend developer needs to know about typography in React and Next.js applications, from picking fonts to setting up a type scale that works across your entire design system.
┌──────────────────────────────────────────────────────────────┐ │ Typography Decision Flow │ ├──────────────────────────────────────────────────────────────┤ │ │ │ 1. Pick a Font ──▶ 2. Set Type Scale ──▶ 3. Line Height │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ One sans-serif Use Tailwind's Body: 1.5-1.75 │ │ (Inter, Geist, built-in scale Headings: 1.1-1.3 │ │ system fonts) (xs through 5xl) Small: 1.5 min │ │ │ │ 4. Line Length ──▶ 5. Font Weights ──▶ 6. Text Color │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ 50-75 chars max 2-3 weights only foreground + │ │ (max-w-prose) (400, 500, 600-700) muted-foreground │ └──────────────────────────────────────────────────────────────┘
Pick One Font (Maybe Two, but Probably Just One)
You don't need five fonts. You don't even need three. One good sans-serif font handles 99% of what a web application needs. Inter, Geist, and system fonts are all excellent choices that work beautifully across every operating system and browser. If you absolutely need a second font, use a monospace for code blocks. That's it. Two fonts maximum.
Why System Fonts Are a Legitimate Choice
System fonts load instantly because they're already on the user's device. No network request, no flash of unstyled text, no cumulative layout shift. San Francisco on macOS, Segoe UI on Windows, and Roboto on Android are all excellent fonts. If you're building a SaaS dashboard or internal tool where brand fonts aren't critical, system fonts are genuinely the best choice for web development performance.
Font Loading Performance Tips
If you're using Next.js (which you probably should be for any serious React project), the built-in font optimization handles everything for you. It downloads fonts at build time, self-hosts them, and eliminates the layout shift that comes with loading fonts from Google Fonts directly. This is one of those things that's trivial with Next.js and annoyingly complicated without it. Always use next/font instead of a Google Fonts link tag.
The Type Scale: Your Sizing System
Don't invent random font sizes. Every size in your application should come from a consistent scale. Tailwind CSS gives you a beautiful built-in type scale that covers every use case. Use it as-is and you'll never have to argue about whether a subtitle should be 17px or 18px. The scale is designed so each step up feels noticeably bigger without being jarring.
text-xs(12px) - Fine print, badges, timestamps, metadatatext-sm(14px) - Secondary text, form labels, captions, sidebar itemstext-base(16px) - Body text. This is your default. Everything starts here.text-lg(18px) - Lead paragraphs, card titles, emphasized contenttext-xl(20px) - Section headings, modal titlestext-2xl(24px) - Page headings, feature section headerstext-3xl(30px) - Hero text, landing page headlinestext-4xl+ - Marketing pages, splash screens, very large displays
The 16px Baseline Rule
Your body text should be 16px. Not 14px, not 15px. 16px. This is the browser default for a reason. It's the size that most people can read comfortably at a normal viewing distance. Going smaller for body text is one of the most common typography mistakes in web development. You might think 14px looks cleaner, but your users will strain to read it, especially on mobile devices. Save smaller sizes for secondary content like labels and metadata.
Line Height: The Secret to Readable Text
Line height (or leading, as designers call it) is the vertical space between lines of text. Bad line height makes text feel either cramped and claustrophobic or floaty and disconnected. Getting it right is one of the highest-impact changes you can make to your UI's readability. Here are the rules I follow in every project.
- Body text: 1.5 to 1.75 (Tailwind's
leading-relaxedorleading-loose). This gives your paragraphs room to breathe. - Headings: 1.1 to 1.3 (Tailwind's
leading-tight). Large text needs less relative spacing. - Small text: 1.5 minimum. Small text with tight line height is basically unreadable.
- UI text (buttons, labels): 1 to 1.25. Single-line elements can be tighter.
Why Headings Need Tighter Line Height
This trips up a lot of developers. At small sizes, you need generous line height because the characters are small and your eye needs help finding the next line. But at large sizes (like headings), the characters themselves provide enough visual anchoring. Too much line height on headings makes them look disconnected, like each line is a separate element. Keep headings at 1.1 to 1.3 line height and they'll look cohesive and intentional.
Line Length: The 65-Character Rule
Lines that are too long are hard to read. Your eye loses its place when traveling from the end of one line to the beginning of the next. Lines that are too short create too many line breaks and make reading feel choppy. The sweet spot is 50-75 characters per line, with 65 being the ideal target. Tailwind CSS has a utility specifically for this.
Font Weight Hierarchy: Less Is More
Font weight is one of your most powerful tools for creating visual hierarchy. But the key is restraint. Two or three weights is enough. More than that and things get messy and confusing. When everything is bold, nothing is bold.
- Regular (400): Body text, descriptions, most content. This is your default.
- Medium (500): Labels, nav items, emphasis within body text, table headers.
- Semibold/Bold (600-700): Headings, titles, important callouts, buttons.
Skip light (300) and black (900) for app UI. They have niche uses in marketing pages but create more problems than they solve in application interfaces. Light weights are hard to read at small sizes, and black weights feel heavy and aggressive.
┌──────────────────────────────────────────────────────────────┐ │ Visual Hierarchy Through Typography │ ├──────────────────────────────────────────────────────────────┤ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ Page Title (text-2xl, font-bold, foreground) │ │ │ │ ════════════════════════════════════════════ │ │ │ │ │ │ │ │ Section Heading (text-xl, font-semibold) │ │ │ │ ──────────────────────────────────────── │ │ │ │ │ │ │ │ Body text in regular weight at text-base size. │ │ │ │ This is where most of your content lives. It │ │ │ │ should be comfortable to read in long blocks. │ │ │ │ │ │ │ │ Secondary text (text-sm, muted-foreground) │ │ │ │ Metadata, timestamps, helper text │ │ │ │ │ │ │ │ [BADGE] (text-xs, font-medium) 12:34 PM │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ Hierarchy = Size + Weight + Color working together │ └──────────────────────────────────────────────────────────────┘
Letter Spacing (Tracking) Tips
Letter spacing is subtle but important. The general rule is: the bigger the text, the tighter the tracking. Large headings look better with slightly negative tracking (letters closer together). Body text looks fine at default tracking. Small uppercase text like badges and labels benefits from wider tracking for readability.
Text Color for Hierarchy
Color is another powerful lever for creating hierarchy. But keep it simple. You really only need two levels for 95% of your UI, plus a couple of special-purpose colors. This works perfectly with Tailwind CSS and design tokens.
- Primary text:
text-foreground- Headings, body text, important information. Full contrast against background. - Secondary text:
text-muted-foreground- Descriptions, timestamps, helper text, placeholders. Reduced contrast for visual hierarchy. - Links:
text-primary- Links and actionable text. Should be obviously clickable. - Danger:
text-destructive- Error messages and validation failures. Red draws attention to problems.
Don't Use Color Alone for Hierarchy
Here's a common mistake: using only color to distinguish between primary and secondary text. This fails for colorblind users and in high-contrast modes. Always combine color with size or weight differences. If your heading and your body text are the same size and weight but different colors, the hierarchy is fragile. Instead, use size, weight, and color together so the hierarchy is clear even in grayscale.
Responsive Typography
Your type scale needs to work across all screen sizes, from tiny phones to ultrawide monitors. The good news is that Tailwind CSS makes responsive typography straightforward with its breakpoint prefixes. The key principle is: body text stays the same size, but headings and hero text can scale down on mobile.
Use Rem, Not Px
Always use rem units (which Tailwind does by default) instead of px. Rem units are relative to the root font size, which means your typography respects user browser preferences. If someone has set their browser default to 20px because they have poor eyesight, rem-based text scales up accordingly. Px-based text ignores their preference entirely. Accessibility isn't just about screen readers. It's about respecting user choices.
Building a Typography Component
For any serious React design system, you'll want a reusable typography component that enforces your type scale. This prevents developers from using arbitrary sizes and keeps everything consistent. Here's a simple but effective approach.
Common Typography Mistakes in Frontend Development
After reviewing dozens of React codebases, I see the same typography mistakes over and over. Here are the biggest offenders and how to fix them.
Using Too Many Font Sizes
If your app has 12 different font sizes, something is wrong. Stick to the Tailwind scale and use 5-7 sizes maximum across your entire application. More sizes don't create more hierarchy. They create confusion.
Ignoring Line Length on Wide Screens
A full-width paragraph on a 4K monitor can easily reach 200 characters per line. That's brutal to read. Always constrain text content with max-w-prose or a similar max-width. Your layout can be wide. Your text lines shouldn't be.
Making Body Text Too Small
14px body text might look sleek on your design, but it's hard to read for extended periods. 16px is the minimum for body content. If you think 16px looks too big, the problem is likely your spacing or line height, not the font size.
Not Testing with Real Content
Lorem ipsum hides typography problems. Test your UI components with real content of varying lengths. What happens when a heading wraps to three lines? What about a single-word heading? How does a paragraph with technical terms and long URLs behave? Real content reveals issues that placeholder text never will.
The Complete Typography Checklist
- One font for your app. Two maximum (sans-serif + monospace).
- Use
next/fontfor automatic optimization in Next.js projects. - Stick to Tailwind's built-in type scale. Don't invent custom sizes.
- Body text at 16px (
text-base). Never smaller for main content. - Body line height: 1.5-1.75. Headings: 1.1-1.3. Small text: 1.5 minimum.
- Keep lines under 75 characters. Use
max-w-prosefor content areas. - 2-3 font weights only: regular (400), medium (500), semibold/bold (600-700).
- Two text colors for most UI:
text-foregroundandtext-muted-foreground. - Tighter tracking on large headings, wider tracking on small uppercase text.
- Use rem units (Tailwind default) so text respects user browser preferences.
- Test with real content at all breakpoints. Lorem ipsum hides problems.
- Build a Typography component to enforce consistency across your design system.
Typography isn't glamorous, but it's foundational. Get these basics right and your entire UI immediately feels more professional and polished. The best part is that these rules are simple and mechanical. You don't need a designer's eye. You just need to follow the system and apply it consistently across your React components. Start with the type scale, set your line heights, constrain your line lengths, and let the hierarchy do the rest.