Component Overrides
Fusion is designed with flexibility in mind, allowing you to tailor core components to your project’s specific needs without ejecting or modifying the library’s source code. This is primarily achieved through the ComponentProvider and the useComponents hook.
The ComponentProvider
The ComponentProvider is a React Context provider that allows you to supply alternative implementations for any of the standard fusion-ui components.
When a Fusion component (or another component that uses useComponents) needs to render a sub-component (e.g., an Input inside a FormField), it fetches its implementation from the ComponentProvider. If no implementation is found, the default fusion-ui implementation is rendered.
When to Override
There are two reasons why you might want to use the ComponentProvider.
- Custom Layout: If you need a
Buttonthat looks significantly different from the defaultfusion-uiButtonacross your entire application and cannot achieve that with just tokens alone. - Custom Logic: Embedding additional analytics, accessibility enhancements, or specific business logic into a standard component.
How to Override
Create Your Component
Your custom implementation needs to be type-compatible with Fusion’s implementation. This means it needs to support at least the same props as the original and forward the ref to the underlying DOM element.
import { ButtonProps } from "@codedazur/fusion-ui";
import { forwardRef } from "react";
import { button } from "./MyButton.css";
export const MyButton = forwardRef<HTMLButtonElement, ButtonProps>(
({ variant = "primary", size = "medium", children, ...props }, ref) => {
return (
<button ref={ref} className={button({ variant, size })} {...props}>
{children}
</button>
);
},
);Although your component’s type must be compatible, you can of course choose to ignore some of the original props, and you can also extend them with additional, optional props, though those will not be used by Fusion itself.
Provide Your Component
Pass a components prop to ComponentProvider. This prop is an object where keys are the names of the fusion-ui components you want to override (as strings, e.g., “Button”, “Card”) and values are your custom component implementations.
import { App, ComponentProvider } from "@codedazur/fusion-ui";
import { MyButton } from "../components/MyButton";
import { lightTheme } from "@codedazur/fusion-ui/style";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<ComponentProvider
components={{
Button: MyButton,
}}
>
<App theme={lightTheme}>{children}</App>
</ComponentProvider>
</body>
</html>
);
}Both @codedazur/fusion-sanity and @codedazur/fusion-ui export a ComponentProvider component. You can use either one, but only the one from @codedazur/fusion-sanity will allow you to override components that are specific to the Sanity integration.
Now, any component that fetches the Button from the component context within this provider’s scope will render a MyButton instead of the default Button.
The useComponents Hook
The useComponents hook fetches component implementations from the ComponentContext. When you build custom components that need to respect these potential overrides (especially if they are meant to be part of your own reusable component library within the project), you should use useComponents.
const { Foo, Bar } = useComponents();It returns an object where the keys are the component names and the values are the actual component implementations to render. By default, the standard Fusion components are returned, but you can use the ComponentProvider to override them.
Imagine you’re building a UserCard for generic use and you want it to support customization of its elements. Instead of directly importing Button, Text, and Surface from @codedazur/fusion-ui, you can use the useComponents hook to get them.
import { useComponents } from "@codedazur/fusion-ui";
export interface UserCardProps {
name: string;
bio: string;
onSendMessage: () => void;
}
export function UserCard({ name, onSendMessage }: UserCardProps) {
const { Button, Text, Surface } = useComponents();
return (
<Surface
padding={400}
border={{ radius: 200 }}
constraints={{ maxWidth: 600 }}
flex={{ direction: "column", gap: 300 }}
>
<Text variant="headline" size="medium">
{name}
</Text>
<Text variant="body" size="small">
{bio}
</Text>
<Button variant="primary" size="small" onClick={onSendMessage}>
Send Message
</Button>
</Surface>
);
}By leveraging the useComponents hook, your UserCard ensures that if Surface, Button, or Text are overridden higher up in the tree via a ComponentProvider, those custom versions will be used instead of the standard implementations.