Skip to content
Vy logo
  • Identitet
  • Spor
  • Resources
Guides

How to Create a React Component

Spor uses Chakra UI as its component library for building and customizing UI components. Before creating a new component or extending an existing one, check the Chakra UI documentation to see if a suitable component or feature already exists. Reusing Chakra's built-in components and patterns helps maintain consistency and reduces unnecessary custom code.

📁 Project Structure

Each component lives in packages/spor-react/src and should follow the same structure as in Figma for easy reference.

A typical component folder contains:

  • ComponentName.tsx – The main component file.
  • ComponentName.test.tsx – The test file (optional but recommended).
  • index.ts – Exports the component.

And for the styling, add it to the /theme/recipes or /theme/slot-recipes folder:

  • component.ts – Defines the component’s styles.

🚀 Step-by-Step Guide

1. Create the Component File

Create a new file in the appropriate folder and name it after the component (e.g., Button.tsx).

import { forwardRef } from "react";
import {
type ButtonProps as ChakraButtonProps,
Button as ChakraButton,
type RecipeVariantProps,
} from "@chakra-ui/react";
type ButtonVariantProps = RecipeVariantProps<typeof buttonRecipe>;
export type ButtonProps = Omit<
ChakraButtonProps,
"size" | "variant" | "colorPalette" // Exclude props from Chakra if needed
> & PropsWithChildren<ButtonVariantProps> & {
/* The props */
}
export const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
const { variant = "primary", size = "md", ...rest } = props;
const recipe = useRecipe({ key: "button" });
const styles = recipe({ variant, size });
return (
<ChakraButton ref={ref} css={styles.root} {...rest}>
{props.children}
</ChakraButton>
);
});

2. Define the Component Recipe

Spor uses Chakra UI's Recipe and Slot Recipe APIs for component styling. The component styles should therefore be placed in a separate recipe.ts file, placed in either src/theme/recipes or src/theme/slot-recipes .

Most styling changes should happen inside the recipe instead of directly in the React component.

ℹ️ Difference between recipe and slot-recipe

Use a recipe when the component consists of a single element. Examples include components like Button and Badge. Read more about Chakra Recipe in Chakra’s documentationEkstern lenke.

Use a slot recipe when a component consists of multiple styled parts. Examples include components such as Dialog, Select, and Menu.

A slot recipe allows each part of the component (called a slot) to have its own styling. Typical slots might include:

  • root
  • trigger
  • content
  • label
  • icon

This makes it possible to style complex components while keeping everything type-safe and consistent. Read more about Chakra’s slot-recipe in Chakra’s documentationEkstern lenke.

📌 Best practices when defining a recipe:

  • Use design tokens instead of hardcoded values for colors, spacing, and typography.
  • Use full names for properties – instead of shorthand like bg or px, use background or paddingX for better clarity.
  • Define defaults in defaultProps instead of in the recipe – sometimes, defining defaultVariant inside the recipe can cause a bug where other variants inherit styles from the default one.
import { defineRecipe } from "@vygruppen/spor-react";
export const buttonRecipe = defineRecipe({
base: {
root: {
fontWeight: "bold",
borderRadius: "md",
},
},
variants: {
variant: {
primary: {
root: {
backgroundColor: "brand.surface",
color: "white",
_hover: {
backgroundColor: "brand.surface.hover"
},
},
},
floating: {
root: {
backgroundColor: "floating.surface",
color: "white",
_hover: {
backgroundColor: "floating.surface.hover"
},
},
},
},
size: {
sm: {
root: {
fontSize: "sm",
paddingX: 3,
paddingY: 2
}
},
md: {
root: {
fontSize: "md",
paddingX: 4,
paddingY: 3
}
},
},
},
});

3. Export the Component

Ensure your component is exported from index.ts.

export * from "./Button";

And remember to add your recipe to the theme system:

import { buttonRecipe } from '../button'
export const recipes = {
button: buttonRecipe,
/* Other recipes */
}

4. Testing the changes

To test your changes, run the following command from the root of the repository.

pnpm dev

This starts the designmanual-frontend application on port 3008.

Once it's running, open http://localhost:3008/ in your browser.

If you've modified an existing component, you can test your changes by using the examples on that component's documentation page. For more advanced testing, use the Playground at http://localhost:3008/playground, where you can write and experiment with your own code.

5. Writing Tests

We use Vitest and Testing Library for testing. Here’s an example of a test:

import { render, screen } from "@testing-library/react";
import { Button } from "./Button";
describe("<Button />", () => {
it("renders correctly", () => {
render(<Button>Click me</Button>);
expect(screen.getByText("Click me")).toBeInTheDocument();
});
});

🔍 Use vitest-axe to check for accessibility violations:

import { axe } from "vitest-axe";
it("is accessible", async () => {
const { container } = render(<Button>Click me</Button>);
expect(await axe(container)).toHaveNoViolations();
});

6. Create a Changeset & Open a PR

Since this is a new component, create a changeset so it gets included in the next release.

npx changeset

Follow the prompts to document the changes, commit the changeset, and push your branch. See the page on Releasing new versionEkstern lenke for a more detailed guide on how to write a changeset.

Once the PR is merged, a Version Packages PR will be generated. Merge that to publish a new release! 🎉

🎯 Best Practices

Use design tokens (brand.primary, spacing.md) instead of hardcoded values.
Keep components small & modular—split complex components into smaller ones.
Use props instead of deeply nested children when possible.
Follow naming conventions (variant, size, colorPalette).
Ensure accessibility—always test with keyboard & screen readers.

For more tips and best practices, read the guide on Best Practices for Component DevelopmentEkstern lenke.

🚀 Now you're ready to build new components in Spor! 🎨