createComponent

The createComponent function create a component that supports conditional styles such as variants. You can also easily create slot components by using the createSlotComponent function.

Overview

createComponent creates a component that supports conditional styles such as variants. The created component can be inherited, has high extensibility, and generates className and displayName, so you can also create components with consistent naming conventions in your project.

Usage

To create a single component, use createComponent, and to create a slot component, use createSlotComponent.

createComponent

To create a single component, use createComponent.

import type { HTMLStyledProps, ThemeProps } from "@yamada-ui/react"
import { createComponent, defineComponentStyle } from "@yamada-ui/react"
const componentStyle = defineComponentStyle({
  base: {
    /* base style */
  },
  variants: {
    /* variant style */
  },
  sizes: {
    /* size style */
  },
  props: {
    /* props style */
  },
  compounds: {
    /* compound style */
  },
  defaultProps: {
    /* default props */
  },
})

type ComponentStyle = typeof componentStyle

export interface ComponentProps
  extends HTMLStyledProps<"div">,
    ThemeProps<ComponentStyle> {}

const {
  component,
  ComponentContext,
  PropsContext: ComponentPropsContext,
  useComponentContext,
  usePropsContext: useComponentPropsContext,
  withContext,
  useComponentProps,
} = createComponent<ComponentProps, ComponentStyle>("component", componentStyle)

export { ComponentPropsContext, useComponentPropsContext }
  • The first argument is the component name used for className and displayName.
  • The second argument is the component style.

Create a component

To create a component, use withContext. Set the argument to the HTML element name or function. withContext uses the provided style and PropsContext props.

export const Component = withContext("div")()
export const Component = withContext((props) => {
  return <styled.div {...props} />
})()

If you don't want to use the provided style and PropsContext props, or want to handle the logic, use component.

export const Component = component((props) => {
  const computedProps = useComponentProps(props)

  return <styled.div {...computedProps} />
})()

Calculate props

withContext and component can perform multi-stage calculations on the provided props.

export const Component = withContext("button")(
  { "aria-label": "Default Label" },
  ({ "aria-label": ariaLabel, ...rest }) => ({
    ariaLabel: ariaLabel === "Default Label" ? "Changed Label" : ariaLabel,
    ...rest,
  }),
)

Transfer props

Style props are filtered after calculating the styling. If you also want to use it in the component logic, use transferProps.

export const Component = withContext(
  ({ size, ...rest }) => {
    return <styled.button data-size={size} {...rest} />
  },
  {
    transferProps: ["size"],
  },
)()

Inherit a component

createComponent created components can be inherited.

import { Component } from "./component"
const additionalComponentStyle = defineComponentStyle({
  base: {
    /* base style */
  },
  variants: {
    /* variant style */
  },
  sizes: {
    /* size style */
  },
  props: {
    /* props style */
  },
  compounds: {
    /* compound style */
  },
  defaultProps: {
    /* default props */
  },
})

type AdditionalComponentStyle = typeof additionalComponentStyle

export interface AdditionalComponentProps
  extends HTMLStyledProps<"div">,
    ThemeProps<AdditionalComponentStyle> {}

const {
  ComponentContext,
  PropsContext: AdditionalComponentPropsContext,
  useComponentContext,
  usePropsContext: useAdditionalComponentPropsContext,
  useComponentProps,
  withContext,
  component,
} = createComponent<AdditionalComponentProps, AdditionalComponentStyle>(
  "additional-component",
  additionalComponentStyle,
)

export { AdditionalComponentPropsContext, useAdditionalComponentPropsContext }
export const AdditionalComponent = withContext(Component)()

This creates an AdditionalComponent that inherits Component.

The difference from the traditional component inheritance is that it can merge ref, class names, styles, and event handlers.

export const AdditionalComponent: FC<AdditionalComponentProps> = ({
  className,
  ...rest
}) => {
  const ref = useRef<HTMLDivElement>(null)

  const onClick = useCallback(() => {}, [])

  return (
    <Component
      ref={ref}
      onClick={onClick}
      className={[className, "additional-component"].join(" ")}
      {...rest}
    />
  )
}

In this case, if ref and onClick exist in the provided props, they will be overwritten. Depending on the logic, it may not work well. To solve this, you need to merge each value and logic for each component.

export const AdditionalComponent: FC<AdditionalComponentProps> = ({
  ref: forwardedRef,
  className,
  onClick: onClickProp,
  ...rest
}) => {
  const ref = useRef<HTMLDivElement>(null)

  const onClick = useCallback(() => {}, [])

  return (
    <Component
      ref={mergeRefs(forwardedRef, ref)}
      onClick={handlerAll(onClickProp, onClick)}
      className={[className, "additional-component"].join(" ")}
      {...rest}
    />
  )
}

By using createComponent to inherit a component, you can automatically merge event handlers such as ref and onClick.

createSlotComponent

To create a slot component, use createSlotComponent. The functionality is the same as createComponent.

import type { HTMLStyledProps, ThemeProps } from "@yamada-ui/react"
import { createSlotComponent, defineComponentSlotStyle } from "@yamada-ui/react"
const componentStyle = defineComponentSlotStyle({
  base: {
    root: {
      /* base root style */
    },
    item: {
      /* base item style */
    },
  },
  variants: {
    /* variant style */
  },
  sizes: {
    /* size style */
  },
  props: {
    /* props style */
  },
  compounds: {
    /* compound style */
  },
  defaultProps: {
    /* default props */
  },
})

type ComponentStyle = typeof componentStyle

export interface ComponentRootProps
  extends HTMLStyledProps<"div">,
    ThemeProps<ComponentStyle> {}

const {
  ComponentContext,
  PropsContext: ComponentPropsContext,
  StyleContext,
  useComponentContext,
  usePropsContext: useComponentPropsContext,
  useStyleContext,
  useClassNames,
  useRootComponentProps,
  useSlotComponentProps,
  withProvider,
  withContext,
  component,
} = createSlotComponent<ComponentRootProps, ComponentStyle>(
  "component",
  componentStyle,
)

export { ComponentPropsContext, useComponentPropsContext }
  • The first argument is the component name prefix used for className and displayName.
  • The second argument is the component style.

Create a component

To create a component, use withProvider and withContext. Each sets the argument to the HTML element name or function for the first argument, and the slot name for the second argument. withProvider uses the provided style and PropsContext props to generate a context. withContext uses the context generated by withProvider to use the style based on the slot name.

export const RootComponent = withProvider("div", "root")()

export const ItemComponent = withContext("div", "item")()
export const RootComponent = withProvider((props) => {
  return <styled.div {...props} />
}, "root")()

export const ItemComponent = withContext((props) => {
  return <styled.div {...props} />
}, "item")()

If you don't want to use the provided style and PropsContext props, or want to handle the logic, use component.

export const RootComponent = component((props) => {
  const [context, computedProps] = useRootComponentProps(props, "root")

  return (
    <StyleContext value={context}>
      <styled.div {...computedProps} />
    </StyleContext>
  )
}, "root")()

export const ItemComponent = component((props) => {
  const computedProps = useSlotComponentProps(props, "item")

  return <styled.div {...computedProps} />
}, "item")()

Use modifiers

To use modifiers, set the slot name to an array.

const componentStyle = defineComponentSlotStyle({
  base: {
    root: {
      /* base root style */
    },
    item: {
      /* base item style */
    },
    start: {
      /* base start style */
    },
    end: {
      /* base end style */
    },
  },
})
export const StartItemComponent = withContext("div", ["item", "start"])()

export const EndItemComponent = withContext("div", ["item", "end"])()

In this case, the style of item and start or end is set, and the class name is "{prefix}-{name}__item--start" or "{prefix}-{name}__item--end".