Building Plume Components with Plume Hooks

Plume provides a set of React hooks that you can use to bring your designs to life. Plume supports hooks for building Buttons, Checkboxes, Select dropdowns, TextFields, Sliders, and more!

What does a Plume component look like?

Using a Plume component feels just like using any other component library — each Plume component comes with a set of well-defined props, with full typescript support. The main difference is that you can design exactly how the component looks and feels using Plasmic, and can easily create additional components variants that suit your need.

What does “Plume-compatible” mean?

Your Plasmic design for a Plume component may need to have:

  • Variants corresponding to different states of the component. For example, for a Checkbox component, you may need a variant that indicates the “checked” state of the Checkbox; for a Select component, you may need a variant that renders the “opened” state of the component.
  • Slots corresponding to contents passed in by props or generated by Plume. For example, for a Button component, you may need a slot for the content of the Button, and a slot for the start icon of the Button.
  • Named elements corresponding to different anatomical parts of the component. For example, for a Select dropdown component, you will need elements that correspond to the root, the trigger button, and the dropdown overlay. Plume needs to know about these elements to attach the appropriate event handlers, and provide the proper ARIA attributes for accessibility.

Using the Plume hooks

For each Plume component, there is a corresponding hook. The general shape of these hooks take these arguments:

  • plasmicClass ー the generated Plasmic* class
  • props ー the component props, which is a subset of the Plume*Props. The Plume hook will automatically apply all variants and args that happen to be in your props for you.
  • config ー configuration Plume needs to activate the right variants or attach the right handlers. Usually a set of variant names for different states, element names that we want to attach props or handlers to, and slot names that we want to fill with generated content.
  • ref ー used with React.forwardRef for imperative programmatic access to the Plume component. Each Plume component comes with its own ref interface; most of the time, there’s a function to get the root DOM element, and a function to focus.

The Plume hooks then return an object with these keys:

  • plumeProps ー an object with the variants, overrides, and args that you should spread onto the Plasmic* component.
  • state ー for components where this makes sense, the current state of the Plume component. You can use this to additionally control the state of the Plume component, or to query for and render additional information for the component. For example, for a Slider, you may want access to the current value of a slider thumb, so you can render it in a tooltip.

Using a Plume hook for your Plume-compatible component often looks like this:

// file: MyComponent.tsx
import PlasmicMyComponent from "./plasmic/PlasmicMyComponent";
import {
  PlumeComponentProps,
  PlumeComponentRef,
  useComponent
} from "@plasmicapp/plume";

// Your component should extend the minimum set of props required
// for this Plume component.
interface MyComponentProps extends PlumeComponentProps {
  // It can additionally have any other variants or slots that
  // you've defined for your component.
  type?: "primary" | "secondary";
  size?: "small" | "large";
}

function MyComponent_(props: MyComponentProps, ref: PlumeComponentRef) {
  // Call the Plume hook
  const { plumeProps } = useComponent(
    // Pass in the generated Plasmic* class
    PlasmicMyComponent,
    // Pass in the component props
    props,
    {
      // Pass in how you map your component variants /
      // elements / props to the Plume spec.

      // Map certain Plume component states to your component's
      // variants

      // Mapping "isDisabled" Plume component state to
      // Variant Group "state", Variant "isDisabled"
      isDisabledVariant: ["state", "isDisabled"],
      // Mapping "isSelected" Plume component state to
      // Variant Group "state", Variant "isSelected"
      isSelectedVariant: ["state", "isSelected"],

      // Map certain required Plume elements to your
      // component's elements
      root: "root",
      label: "labelContainer",

      // Map certain required Plume slots to your
      // component's slots
      contentSlot: "children"
    },
    ref
  );

  return (
    <PlasmicMyComponent
      // Spread the `plumeProps` onto your Plasmic* component
      {...plumeProps}

      // useComponent automatically applies your variants and
      // args found in your props, so you don't need to do it
      // yourself.

      // But you may have other parts of your component that
      // Plume doesn't know about that you'll want to provide
      // overrides for
      someNode={{
        // override props
      }}

      // You may also have other variants to activate based
      // on your props
      withIcon={!!props.icon}
    />
  );
}

const MyComponent = React.forwardRef(MyComponent_);
export default MyComponent;

React-Aria and friends

Under the hood, Plume wraps around hooks provided by the great React-Aria project, an exciting new framework for attaching accessible behavior to your components. With Plasmic, it is now easier than ever to build your own fully custom component system — you make the designs in Plasmic, React-Aria brings the accessibility and interactivity, and Plume brings the two together! Read more about how this works.