Implementing a Design System: Challenges, Solutions, and Best Practices

Why We Needed a Design System As we transitioned to a new tech stack, we simultaneously aimed to improve team efficiency. One of the key steps in this direction was implementing a unified design system for all company projects.
A design system is a library of reusable interface elements, typography, and visuals (such as color palettes, icons, and buttons) that help standardize and accelerate development. While design systems primarily benefit developers rather than product owners, they are crucial for maintaining consistency and efficiency across projects.
We initially explored open-source design systems, but they lacked the flexibility required for our projects. So, we opted for customization, and this article details our journey. As expected, we encountered various challenges and limitations—but spoiler alert: we successfully completed the MVP phase!
Choosing a Foundation: Why Material UI? We saw no reason to build a design system from scratch since there were already plenty of open-source solutions available. After reviewing both local and international options, we chose Material UI (MUI) by Google as our foundation.
Why MUI? ✔️ React compatibility – Our projects use React, making MUI a natural fit. ✔️ Material Design principles – We appreciate the design philosophy behind Material UI. ✔️ Cross-browser and adaptive design – Ensures seamless responsiveness. ✔️ Active community and regular updates – MUI has extensive documentation and ongoing support. ✔️ Prior experience – Our team had already worked with MUI successfully.
Material UI is highly customizable, but to maintain consistency, we had to ensure that our custom components aligned with its design rules, typography, and color schemes—while avoiding redundancy.
Development Process Our need for a design system became evident while working on a PWA application. The development of both the application and the design system happened in parallel, following a roadmap with three stages: MVP, MMP, and the full version.
MVP Stage At the MVP stage, the app's main screen displayed only essential information. Similarly, we developed only the most basic components with the goal of simplicity and future scalability.
Scaling Issues However, when we moved to the MMP and full versions, many components required modifications. Some even had to be rewritten entirely because their initial structure was no longer optimal. This introduced a challenge: every significant change required reviewing all dependent scenarios, sometimes leading to unexpected complexity.
One of MUI’s drawbacks is its deeply nested HTML structure, making it hard to track which elements control what. Despite these difficulties, our approach was the least resource-intensive given our tight deadlines.
Since business requirements evolved dynamically, planning everything in advance was impossible. Instead, we embraced iterative development, even though it meant frequent revisions. While this aligned with agile principles, it also required constant internal communication—which brings us to the next challenge.
Team and Communication Team Structure Ideally, a dedicated team should manage a design system, but we couldn't allocate resources for that. Instead, whoever was available contributed. This included designers and frontend developers, with a total of five people involved.
The process started with designers, with only one person initially handling the task. Luckily, this designer had development experience, which accelerated decision-making and improved collaboration with developers. Later, more designers joined the effort.
Tools We used:
Figma – For designing, documenting properties, and maintaining a component library. Storybook – A React-friendly UI tool that isolates components, making them easier to develop, test, and document. We started with basic Storybook features but plan to expand its usage. Collaboration Workflow To maintain consistency, we established regular sync meetings between designers and developers. Key activities included:
Designers reviewed developer work to ensure UI consistency. Developers provided feedback on design feasibility. Lead developer reviews ensured quality before merging code into the repository. To avoid duplication, we assigned one developer per component, meaning that whoever started working on a component had to finish it—even complex ones like autocomplete with multiple UI states.
To improve organization, we eventually appointed a “design system owner”, responsible for reviewing components, maintaining a backlog, and ensuring system integrity.
Accessibility Implementation Although accessibility wasn’t a strict requirement from stakeholders, we incorporated it where possible, as we planned to reuse the UI kit in other projects.
How We Approached Accessibility Designers predefined accessibility parameters and annotated them in Figma. Developers received notes on where to add relevant accessibility tags to improve usability for all users. Testing Strategy Since we lacked dedicated QA testers, we relied on automated testing to maintain quality.
Snapshot Testing One of our primary methods was snapshot testing, which compares the current UI state to a reference screenshot. This approach was particularly useful given MUI’s hidden elements (e.g., Touch Ripple effects).
However, snapshot testing has a downside: any minor UI change generates hundreds of snapshot updates, making it tempting to approve them all without proper review.
Our Solution We limited snapshot tests to complex components where manual UI verification would be difficult. Additionally, we implemented a notification system in Mattermost to alert us of breaking changes.
Key Takeaways Customizing an existing system is often more efficient than building from scratch—but expect trade-offs. Iterative development is necessary for evolving requirements, but it demands strong internal communication. A dedicated team would be ideal, but a well-structured workflow can make distributed contributions effective. Testing is crucial—especially when working with libraries like Material UI, which introduce hidden complexities. Accessibility should be integrated from the start, even if not initially required. 🚀 In the end, we successfully built and deployed our custom design system, significantly improving the efficiency of our development process. While challenges arose, our approach ensured consistency, maintainability, and scalability for future projects.