Advanced: Using Components as Props

Advanced Usage

The below showcases three different approaches to using Components as Props. Currently, this feature is only available for our React renderer.

  • Typescript configuration (with infer spec enabled)
  • Manual configuration within the Knapsack UI
  • Manual configuration by editing code (the pattern data file associated with

Method #1: Typescript Configuration (with infer spec enabled)

  1. Verify your component is using infer spec (see this guide if you need help)
  2. When defining your prop for passing components be sure to use either React.FC or React.VFC (as shown in the examples below)
  3. In the component that is taking Components as Props — be sure to convert the prop to the component format with callToAction: CallToActionSlot (React components must be capitalized)
  4. Finally, add the component to the return (<CallToActionSlot>Text Here </CallToActionSlot>)
  5. All that is left now is, within the Knapsack UI, select the component to pass in as a prop!

Consider this example Chip component:

import * as React from 'react';
import clsx from 'clsx';

export interface ChipProps {
  children: React.ReactNode | string;
  color?: 'neutral' | 'info';
  size?: 'small' | 'medium';
  className?: string;
}

export const Chip: React.FC<ChipProps> = ({
  children,
  color = 'info',
  size = 'medium',
  className,
}: ChipProps) => {
  const classes = clsx(
    className,
    'ex-chip',
    `ex-chip--${color}`,
    `ex-chip--${size}`,
  );

  return <div className={classes}>{children}</div>;
};

Now consider this Card component that is able to use a Component as a Prop ( callToAction):

import * as React from 'react';

type ImageSrc = string;
type Title = string;
type Subtitle = string;

export interface CardProps {
  img?: ImageSrc;
  title?: Title;
  subtitle?: Subtitle;
  children?: React.ReactNode;
  /**
   * Text used with the optional callToAction slot
   */
  ctaText?: string;
  /**
   * Optional Call To Action. Ideal for Chips and Buttons
   */
  callToAction?: React.FC;
}

export const Card: React.FC<CardProps> = ({
  title,
  subtitle,
  children,
  img,
  ctaText,
  callToAction: CallToActionSlot,
}) => {
  return (
    <div className="ex-card">
      {img && <img src={img} className="ex-card_img" alt="" />}
      <div className="ex-card_body">
        {title && <h4 className="ex-card_title">{title}</h4>}
        {subtitle && <h6 className="ex-card_subtitle">{subtitle}</h6>}
        {children && <div>{children}</div>}

        {CallToActionSlot && (
          <div
            style={{
              marginTop: '1rem',
            }}
          >
            <CallToActionSlot>
              {ctaText ?? 'Default CTA text'}
            </CallToActionSlot>
          </div>
        )}
      </div>
    </div>
  );
};

Given the above examples — we are able to use our callToAction prop to pass in our Chip component. We could do the same for a Button, Link, or any other component that best fits our need.

Method #2: Manual Configuration (in the UI)

Note: For this example we will use the same architecture as the above Typescript example with callToActionManual: CallToActionSlot (React components must be capitalized).

  1. Within your browser in the Knapsack UI — navigate to the pattern you'd like to add the Component as a Prop.
  2. Edit the component spec by clicking the gear icon:
  3. Scroll down to the "Slots" spec and add a new slot:
  4. For our example, we'll add a new slot called callToActionManual. Toggle open the configuration of the new slot and enable "Component as a prop" (you'll note enabling this option also sets the "One item per slot" and "Disallow text" options, which are required for this to work correctly:
  5. Click the purple "Save Changes" button and then close the overlay by clicking away from it.
  6. In the UI, select a component to pass as the prop and enjoy! 🎉

Method #3: Manual Configuration (in the code)

  1. Locate and edit the data file associated with your pattern (for example, knapsack.pattern.card.json)
  2. In the "spec", within the specific "slots" item you’d like to use as a prop — add the following three flags: 

    "allowOnlyOne": true,
    "disallowText": true,
    "isTemplateReference": true
    	
    For a slot called "component", it should look like the below:

    "spec": {
      "slots": {
        "component": {
          "title": "component",
          "allowOnlyOne": true,
          "disallowText": true,
          "isTemplateReference": true
        }
      }
    }
    	
  3. Next, navigate back to your component within the Knapsack UI and add your component to be used as a prop!
  4. Have fun using Components as Props! 🎉