ETC Easy To change React. Part 1 The importance of Abstracting your UI library.

Nate Hargitt
2 min readMar 22, 2024

React empowers developers with phenomenal flexibility, including the ability to integrate a wide array of third-party UI component libraries like Material UI (MUI), Ant Design, or even a custom, in-house solution. However, tightly coupling your application’s components directly to an external library can create challenges in the future.

The principle of “Easy to Change” (ETC) from “The Pragmatic Programmer” advocates for creating software that embraces adaptability. By establishing an abstraction layer between your application’s code and your chosen UI library, you introduce several compelling benefits:

Benefits of Abstracting External UI Libraries

  • Framework Agnosticism: An abstraction layer insulates your application from the specifics of the underlying UI library. This grants you the freedom to switch libraries later on if needed, without needing to rewrite your entire frontend codebase.
  • Simplified Migrations: Library updates might introduce breaking changes. Abstracting your UI components provides centralized points of modification, streamlining the upgrade process and minimizing cascading effects.
  • Enhanced Testability: Your custom interface layer can be unit tested independently of the external library, leading to more robust tests and less dependency on third-party code.
  • Tailored Components: Abstracts enable you to extend and customize components from external libraries, ensuring they adhere perfectly to your project’s design guidelines and specific requirements.

How to Implement an Abstraction Layer

  1. Component Wrappers: Create simple React components that encapsulate the functionality of components from your external library. These wrappers provide a consistent API for your application to interact with.
  2. Custom Theming: Define an abstraction for applying your application’s styling, fonts, and color schemes. Implement this theming mechanism independently of the external library’s theming solution.
  3. Utility Functions: For common interactions or behaviors, write utility functions that interact with components from the external library without exposing its API directly.

Example: Abstracting a Dialog Component

JavaScript

// DialogWrapper.js
import React from 'react';
import { Dialog, DialogTitle, DialogContent } from '@mui/material'; // Example with MUI

const DialogWrapper = ({ open, onClose, title, children }) => {
return (
<Dialog open={open} onClose={onClose}>
<DialogTitle>{title}</DialogTitle>
<DialogContent>{children}</DialogContent>
</Dialog>
);
}

The Key Idea

Your DialogWrapper exposes a simplified and generic API. If you decide to switch from MUI to a different library, you'd primarily need to adjust the implementation within this wrapper. Your application's core logic wouldn't be significantly affected.

The Pragmatic Advantage

By following these guidelines, you create a maintainable, adaptable, and future-proof React codebase. Your investment in an abstraction layer yields returns in reduced maintenance overhead and enhanced flexibility when it comes to UI library choices.

Let me know if you’d like to dive into more specific examples or discuss the tradeoffs associated with this approach!

--

--

Nate Hargitt

Senior Front End Engineer at Convertly. Hoping to make the internet a faster place.