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"
import type { HTMLStyledProps, ThemeProps } from "@/components/ui"
import { createComponent, defineComponentStyle } from "@/components/ui"
import type { HTMLStyledProps, ThemeProps } from "@workspaces/ui"
import { createComponent, defineComponentStyle } from "@workspaces/ui"
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 }
defineComponentStyle is a function that defines the component style. This function has an important role in type completion.- The first argument is the component name used for
classNameanddisplayName. - 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.
variants, the variants of the inherited component will take precedence.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"
import type { HTMLStyledProps, ThemeProps } from "@/components/ui"
import { createSlotComponent, defineComponentSlotStyle } from "@/components/ui"
import type { HTMLStyledProps, ThemeProps } from "@workspaces/ui"
import { createSlotComponent, defineComponentSlotStyle } from "@workspaces/ui"
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 }
defineComponentSlotStyle is a function that defines the component style. This function has an important role in type completion.- The first argument is the component name prefix used for
classNameanddisplayName. - 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".