In this guide, we will go over the best practices for integrating your Subframe designs into your codebase.
Overview
Subframe uses a copy / paste workflow for exporting code. That means the code for your designs lives in your codebase, and not in a hosted solution like Webflow or Retool.
Here are three tips you should know about this workflow:
1. Components are synced, pages are copy / pasted
Most people use Subframe as the source of truth for their design system. To do this, we recommend using the CLI to sync changes to components to your codebase.
Pages often require refactoring and additional business logic after export. For that reason, pages are never synced via CLI and always copy / pasted.
2. There is no “wrong way” to use Subframe
We find teams often ask if they are doing something wrong. Our answer is if it saves you time – then you are using Subframe correctly.
Another common question is whether everything needs to be Subframe. The answer is no. It’s perfectly ok to have components that aren’t in Subframe yet or you disabled sync for.
3. Use source code as an escape hatch
Subframe is unique in that we give you the source code for everything. The generated code is fully built on open-source libraries like Radix and released on Github as SubframeCore.
Having the source code is important because it avoids vendor lock-in and gives you full flexibility and control in code. If you ever need a feature Subframe doesn’t support yet, you can always create a workaround in code.
Adding business logic
A core principle behind Subframe’s code export flow is that business logic (like making an API call or managing React state) belongs in code, not a design tool. Thus, Subframe does not export any business logic, other than UI-specific interactions like animating an accordion open / close.
Pages
The typical flow for exporting and adding business logic to a page is:
Click on the Code tab on the top-right of the editor to open the code panel.
Copy / paste the CLI command to sync the necessary components
Copy / paste the entire page code
Refactor the code / add your business logic as you see fit
You can find our full guide here
Components
It’s best practice to treat your Subframe components as “dumb” components. You rarely want any application-specific logic in a design system. Instead, business logic will usually be on the page-level.
However you may sometimes want to refactor business logic into a component. Here are two ways you can do that in Subframe.
1. Wrap your “dumb” components (recommended)
We recommend you create a new component that wraps your “dumb” components with business logic. All Subframe components will forward its props to its root React element, meaning you can always add onClick
and other handlers as React props. By using this pattern, your components will never be out of sync with Subframe.
For example, you may create a CustomButton like so:
import React from "react";
import { Button } from "@/subframe/components/Button";
interface CustomButtonRootProps extends React.ComponentProps<typeof Button> {
className?: string;
}
export const CustomButton = React.forwardRef<HTMLButtonElement, CustomButtonRootProps>(
function CustomButtonRoot(
{ className, ...otherProps }: CustomButtonRootProps,
ref
) {
/**
* Add any hooks or custom logic here
*/
return (
<Button
className={className}
ref={ref}
onClick={() => {
// You can handle events here too
}}
{...otherProps}
/>
);
}
);
Sometimes your Subframe component has interactive elements like buttons or dynamic content. We recommend you add a slot for those scenarios. Read our full guide on slots and composition here.
2. Disable sync
As a last resort, you can always add your business logic directly to the source code. To prevent the CLI from overwriting your changes, add the following to the top of the file to disable sync:
// @subframe/sync-disable
Any further changes to the component will need to be synced manually.