Back to Blog

How to create a design system (in 2025)

Introduction

Whether you're working on a startup's MVP or evolving an enterprise platform, a thoughtfully created design system can dramatically improve both the efficiency of your team and the consistency of your user experience.

The most successful design systems aren't the most complex or visually impressive—they're the ones that facilitate seamless collaboration between designers and developers while adapting to the unique needs of your team and product.

Starting a New Project

When deciding how to create a design system, I first assess:

  • Team: Are there other designers, full-time developers, or development agencies involved? If multiple designers are present, a robust Figma library becomes essential, while developer-only teams might prioritize code-based solutions.

    The team structure drastically impacts how formal your system needs to be. On one project, I worked with an external agency in Poland with 30 developers, 10 of whom would join calls. This created a significant disconnect, making a comprehensive design system crucial.

    Conversely, when working with close-knit teams or highly experienced engineers, a less formal approach often suffices.

  • Engineer experience and motivation: Sometimes it's less about experience and more about motivation. I worked with an engineer who had 15 years of experience but wasn't motivated to "connect the dots" between design and implementation. Even experienced teams benefit from clear design systems when motivation or bandwidth is limited.

  • Existing process: For established projects, what systems are already in place?

  • Tech: For older codebases, there may be preferred processes or established patterns; new projects typically use modern React stacks that are more open to opinionated design systems.

The scope varies significantly across projects. For startups, the design system often emerges as a byproduct of core product design rather than existing as a separate initiative. This organic approach can be more resource-efficient for teams with limited bandwidth.

For existing projects, I audit the current product and identify all components required. With new products that have established branding, it's best to iterate freely on key screens first, then extract patterns and separate into components and variables once the design feels right.

Building Good Foundations

The decision between starting from existing systems versus building from scratch depends on company or project maturity:

  • New startups (SaaS, webapps) typically use similar stacks (TailwindCSS, Shadcn, React), making this a solid starting point for 0→1 projects.

    When working with startups looking to validate quickly, I've found that suggesting these technologies during kickoff calls is almost always met with agreement, as most modern teams were planning to use them anyway. This alignment creates an immediate shared understanding between design and development.

  • Larger companies often have technical debt with existing CSS, which calls for more custom solutions while borrowing patterns from modern frameworks (variables, components, tokens). In these environments, a gradual transition approach often works better than a complete overhaul.

  • Mobile development lags behind in design systems, with platforms divided between Swift (iOS), Kotlin (Android), and various frameworks (Expo, Capacitor, Flutter). Ionic provides a good foundation, though leveraging native UI elements typically makes more sense for performance and platform consistency.

    Unlike web development, mobile lacks standardized frameworks that everyone uses. There's no equivalent to Tailwind for mobile, making cross-platform consistency challenging. Even when using hybrid approaches like Capacitor or React Native, these represent just a fraction of possible implementation approaches.

When building from scratch, I recommend starting with a minimal viable design system—focus on core components and patterns first, then expand as needs arise. This prevents analysis paralysis and keeps the system aligned with actual product requirements.

Design System Options

Untitled UI

For Figma, Untitled UI offers the most comprehensive design system:

  • Well-designed, though historically lacking a code counterpart (currently in development)
  • Colors align closely with TailwindCSS, without using a semantic variable system
  • Provides a quick start with base components, though in 2025 I'd generally recommend options that better align with development
  • Excellent for teams that need to quickly establish a professional visual language without reinventing the wheel

shadcn/ui + Tailwind

Most new startups currently use this combination:

  • TailwindCSS: A framework letting developers define styles directly in HTML using utility classes (e.g., text-xs for 12px text, pl-4 for 16px left padding)
  • shadcn/ui: A UI kit with unstyled, accessible primitive components including built-in states, keyboard navigation, and screen reader support
  • Uses a default semantic palette (foreground, background, border)
  • For rapid development, aligning to these two prevents confusion and streamlines design-to-development workflow
  • Particularly well-suited for teams where developers outnumber designers

One significant advantage I've found is that AI is surprisingly good at working with shadcn/ui components. When developers use AI coding assistants (which many do now), these tools are well-trained on shadcn/ui and Tailwind, making implementation faster and more consistent than with custom or less popular systems.

The developer experience with this combination is great, with minimal context-switching required and strong performance characteristics.

Material UI

An older but widely-used UI kit with both Figma components and a React library:

  • Less refined foundational UI design compared to newer alternatives
  • Often familiar to experienced development teams, making it a practical starting point
  • Comprehensive documentation and large community support
  • Better suited for enterprise applications where Google's design language is acceptable

Material UI's extensive history means it has solved many edge cases and accessibility concerns, though it can feel dated compared to more contemporary design systems.

Icon Libraries

A consistent icon system is crucial for visual coherence. These libraries offer good starting points:

  • Lucide: Open source, consistent style, good variety for common UI needs
  • Untitled UI Icons: Premium option with extensive coverage
  • Radix Icons: Minimal style, works well with shadcn/ui and modern interfaces
  • Material Icons: Massive library, recognizable style, works seamlessly with Material UI

I've found that while it's tempting to find the perfect icon set in Figma, developers often push back if the chosen library isn't available as a code package. They'll typically replace your carefully selected icons with ones from a library they can easily implement, so aligning on an icon system early prevents these substitutions later.

Evaluating Design Systems

A good design system primarily simplifies communication between designers and developers. Beyond this, consider:

  • Visual coherence and consistency: Does it maintain a unified look and feel across all touchpoints?
  • Ease of use and documentation quality: Can team members easily find and implement components?
  • Accessibility compliance: Does it meet WCAG AA standards out of the box?
  • Value for both designers and developers: Does it streamline workflows for all disciplines?
  • Extensibility: Can it grow with your product without requiring a complete overhaul?

Regular feedback sessions with both designers and developers can help identify friction points and refinement opportunities. The best design systems evolve continuously rather than being treated as one-time projects.

Tokens and Variables

While seemingly abstract, tokens and variables accelerate design/development, improve consistency, and enable multiple themes:

  • Using text-muted-foreground instead of deciding between gray-500 or gray-600 saves time and reduces frontend errors
  • For startups, shadcn/ui and Tailwind provide solid foundations with minimal setup
  • For more complexity, Vercel's Geist Design System demonstrates effective variable usage
  • For larger organizations, GitHub's Primer Design System exemplifies complex variables within a relatively simple system

Implementing a token-based approach from the beginning sets the foundation for future theme expansion, dark mode support, and brand refreshes without requiring massive refactoring.

Cross-Platform Considerations

When creating systems for both web and mobile:

  • Exporting colors from Figma to JSON simplifies implementation across web, iOS, and Android
  • Tools like Style Dictionary can sync colors to different platforms, though developers may hesitate to adopt anything directly connected to the codebase
  • Consider platform-specific patterns and constraints—what works well on web might feel awkward on mobile

On one project where we were developing web, iOS, and Android applications simultaneously, I pushed for using Style Dictionary to synchronize our color tokens across platforms. While the developers were hesitant to use anything that automatically synced with the codebase, they were happy to manually implement the JSON exports from Figma. This provided basic cross-platform consistency without introducing unfamiliar tools to their workflow.

Creating a "core" token system that feeds into platform-specific implementations often works better than trying to enforce identical experiences across all platforms.

Maintaining and Scaling

Once established, maintenance should require minimal time—primarily involving adding or updating Figma pages and publishing the library.

The level of maintenance varies dramatically with project scale. For a two-week startup project, maintenance might be daily calls with developers to clarify implementation details. For a six-month enterprise/government project, a more formal documentation approach becomes necessary.

Establish clear governance around who can modify the system and what process changes should follow. Having designated "design system champions" on both design and development teams helps ensure continued adherence and evolution.

Storybook

For code component maintenance:

  • Integrates directly with the codebase
  • Developers create example usage "stories" that serve as living documentation
  • Often poorly maintained since it's managed by developers (not designers)

I've found that almost every existing project I join has a Storybook instance, but it's frequently three years old and hasn't been updated. Developers typically don't prioritize maintaining visual documentation, which is why alignment on simpler, standard components often proves more effective in practice.

When implemented well, Storybook creates a shared reference that reduces misinterpretations and implementation errors. However, it requires commitment from the development team to remain current.

Layout

Having found that Storybook was rarely successful in creating a source of truth for design systems, I decided I wanted a way for designers to create and maintain design systems in code.

That's what Layout is for, once the designer has finished designing a component in Figma, they can paste the link and add it to Layout - which becomes the source of truth (in code), creating a shared environment for both design and development.

Common Pitfalls

Be aware of these frequent challenges:

  • Designer overcomplication: Beautiful, elaborate design systems that developers don't understand or can't implement efficiently miss the point. I've seen designers spend weeks creating perfect Figma components that developers never use because they don't align with the implementation approach.

  • Lack of design team confidence when providing feedback or addressing developer oversights. Understanding even basic implementation details (like Tailwind's spacing system using multiples of 4px) helps designers provide more effective feedback.

  • New designer constraints: Team additions may feel limited by existing system boundaries. Allow for experimentation before enforcing constraints.

  • Premature optimization: Creating extensive component libraries before understanding actual product needs. I follow a "diverge then converge" approach—experiment freely at first, then systematize once patterns emerge.

  • Inadequate documentation: Systems that exist but aren't properly documented or easily accessible. However, I've found that excessive documentation often goes unread—developers typically prefer brief conversations to extensive documentation.

Successful implementation requires ongoing communication, pragmatism, and a willingness to prioritize practical outcomes over theoretical perfection.

Conclusion

Creating an effective design system is less about following a specific formula and more about understanding your team's unique context and needs. The best systems emerge through thoughtful collaboration between designers and developers, with a shared commitment to improving both the product experience and the creation process.

Start small, focus on the components and patterns that appear most frequently in your product, and build from there. Remember that a design system is never truly "finished"—it should evolve alongside your product and team, continuously improving to meet changing requirements.

When approaching a new project, have pragmatic conversations about goals and timeline. For a startup needing to launch in two weeks, a lightweight system built on existing patterns makes sense. For enterprise projects spanning months, a more comprehensive approach becomes worthwhile.

Whether you choose a prebuilt solution like shadcn/ui or craft something completely custom, success ultimately depends on adoption. The most beautiful, comprehensive design system is worthless if your team doesn't use it. Prioritize clarity, simplicity, and practical value above all else, and you'll create a system that genuinely improves your product development process.