Decoupling my components from the UI system

141 Views Asked by At

So, my goal is to have my React components not coupled to the UI system's elements. I want to be able to "quickly" move from, let's say, Mantine to MUI.

Here's what I could achieve so far.

tsconfig.json:

{
  "compilerOptions": {
    ...
    "baseUrl": "./src",
    "paths": {
      ...
      "@ui/*": ["ui/*"]
    }
  }
}

Related files:

ui
├── autocomplete.ts
├── button.ts
├── index.ts
├── provider.ts
├── icons
│   ├── icon.ts
│   ├── index.ts
│   └── tabler
│       └── copy.tsx
└── system
   ├── index.ts
   └── mantine
       ├── autocomplete.tsx
       ├── button.tsx
       ├── index.ts
       └── provider.tsx

The ones in the ui root directory will only describe an interface, like so: ui/button.ts:

export interface ButtonProps {
  text: string;
  onClick: () => void;
  rightIcon?: React.ReactElement;
}

The ones in the ui/system/[mantine|mui|...] will be responsible to render the UI component, implementing the respective interface. When necessary I'd tweak the parameters in order to compy to the UI system's component props. Here's an example:

import { Button } from '@mantine/core';
import { ButtonProps } from '@ui/button';

const MantineButton: React.FC<ButtonProps> = (props: ButtonProps) => {
  const { text, onClick, rightIcon } = props;

  return (
    <Button onClick={onClick} rightIcon={rightIcon}>
      {text}
    </Button>
  );
};

export default MantineButton;

All the implemented components are exported in the respective UI system folder index.ts, where I rename then to a common simple name:

export { default as UiSystemProvider } from './provider';
export { default as Autocomplete } from './autocomplete';
export { default as Button } from './button';

I then choose whichever UI system I want to use by re-exporting everything that is being exported in the UI system's directory:

export * from './mantine';
//export * from './mui';
//export * from './bootstrap';

Finally, I can have my client components to import an UI component from @ui/system and this would hopefully reach the correct UI component from the UI system that is currently being used by the project, like so:

import { Autocomplete, Button } from '@ui/system';

So, this kind of works, but my question is: what would be a more Reactish way of achieving this? I am coming back to React after a while away...

I don't plan to have 2+ UI systems in the project when it goes live, I just want to be able to quickly move from one to another if I decided to, so this was the way I've found to reduce the coupling.

I'll appreciate any tips you can give me.

Thank you

0

There are 0 best solutions below