Skip to main content

Create a component

Overview

Creating a component differs from "just" adapting a component in that it starts out without a specific kickstartDS component already in mind.

All but the most obvious components will be implemented following this strategy. And it's the preferrable way to do it, too. You should start out with just a concept of what you actually need. Which components can help in bringing that concept to life should be something you concern yourself with second.

This also explains why this has an additional step in "Component Mapping", when compared with adapting a component. This is where we will try to match our component API to existing components.

This works especially well in 2 cases:

  1. When what you need is actually just a specialized case of one of our existing components. Like when wanting to create an article teaser, you'll probably find a general purpose kickstartDS component like the TeaserCard that matches this signature really well
  2. You have a rather complex component (think something like a more advanced blog head component). Often you'll find those components to be composed of basic building bricks (like Buttons, TagLabels or Headlines) after all, which is where component mapping to kickstartDS comes into the picture again
When zooming in enough, everything starts to look the same

If you think about it, there's only a limited number of ways information can actually be presented. And oftentimes you'll find there's already preferred versions of doing it out there, what we'd call learned user interface (UI), or just best practices. The Hamburger menu icon would be such an example. Or how certain information is often teased in the form of structured cards.

Adhering to that expectation, and not creating unneeded artificial friction, is one big part of delivering a good user experience (UX). We're convinced we can cover a great range of those basic building blocks for you. Allowing you to concentrate on destructuring your requirements, mapping the resulting pieces to kickstartDS components. It's how bigger kickstartDS components like a Storytelling component (using Button, Headline, RichText, etc) are constructed, too.

Additionally this can of course also be combined with customization for your own added properties, not part of the used kickstartDS base components (see our Headline component example for that). Or be implemented using extension if the component should expand on some kickstartDS base component (see our Section component example for that).

To learn more about the different processes available to you when creating a component with kickstartDS, have a look at the overview page of this section.

Creation process

You should already have an idea of what you'll need from your component, or some rough image of what it should do. We start by refining those requirements into a component API, which we'll then map to existing kickstartDS base components. This will then serve as our starting point for creating the actual component.

There are three main steps in creating a component:

  1. Component definition,
  2. Component mapping, and
  3. Component creation

So the main way this differs from adapting a component is in adding "Component Mapping", in between "Component definition" and "Component creation". This is where we'll try to match our component API to existing components.

We'll also slightly deviate from it when defining our structure, as we can't just reduce an existing set of component properties here.

Adaptation process as a base line

If you've read our "Adapt a component" guide you probably already know this:

This guide expects you to reduce the set of props offered by kickstartDS components, when used as base components. We'll also skip over, or significantly shorten, parts already covered by that guide. If unsure about something, best cross-reference it!

Let's start with the definition!

1. Component Definition

Like the adaptation process we start by defining a component API. But unlike it, we directly write down what we need. We don't start with a blueprint this time.

We'll use the TeaserCard component throughout this guide to illustrate concepts. This will not be an exhaustive example, though. For that have a look at our guide "Create TeaserCard component". We also have an additional example in "Create Interstitial component", but to actually implement that you need access to our kickstartDS content module. But following the thought process behind it can provide values nontheless.

kickstartDS Content Module

The perfect addition to our Open Source base

The Content Module includes seven rich components: Hero visual, Quote, Storytelling etc. — everything you need to build beautiful content experiences or to enrich your existing Design System
Interested? Contact us!

Purpose

When thinking about a component that should be part of your Design System, you should already have a strong sense of what that component should be, and what it should accomplish for you. If that's not the case, it probably isn't stable enough yet to be part of a Design System that should find wider adoption. You should probably take a step back first, and maybe start a Design System Initiative to narrow down on what components you'll really need.

With that in mind, let's assume that the purpose of our component (the TeaserCard) is to tease content. It should look like a card while doing that, and have the obvious features one would assume a card based component to have. Like a cover image, a link, a headline and some text... with some of those potentially being optional. And because our Design System has an invert feature, it should also be invertible.

Structure

Before we start implementing, we'll want to define a first rough draft of our component API. Defining a name, a small description and a rough type, per property, goes a long way in keeping the focus on the core of your component.

Let's keep using the TeaserCard as an example, with the following properties being the result of pinning down our rough idea:

PropertyTypeDescription
headline *stringHeadline for the teaser card
text *stringBody text for the teaser card
imagestring(Optional) image to display as cover
invertedbooleanShow inverted version of card?
target *stringTarget that should be linked

Fields that should be required are marked with a *.

2. Component Mapping

This is the part unique to this way of implementing a component (compared, for example, to adapting a component). We'll match the component to one, or more, kickstartDS base components... and potentially make some slight adjustments.

Matching it

Matching one or more components means finding components that are part of kickstartDS that can cover your use case, or one part of it. This way you create your component by composition.

For our TeaserCard, when looking through all of the existing kickstartDS base components (one great way of doing this is through our Storybook), we identify the TeaserBox as a great candidate. It:

  • has a topic that can serve as our headline
  • takes text for the body copy
  • already includes options for a cover (image), and even some options for it (ratio, imageSpacing)
  • can be inverted
  • and takes a Link component for link

This seems to match really well!

Adjustments

While just matching a component can already get you pretty far, oftentimes you'll want to make some more adjustments to really make everything fit for you. And while the properties may match up really well, there's still some translation to do to make it work.

Comparing TeaserCard and TeaserBox... we don't have to do anything for text, inverted or image, as these already share a name and definition. In the case of headline we just have to rename the prop (from topic). And for target we simplify the existing link object to a single string property... hard coding the rest of the values in the process, while also renaming it.

3. Component Creation

In the third and final step we'll get to actually create our component. We'll encode the component API by creating its JSON Schema, and create a React template matching our selected properties to the kickstartDS base component.

JSON Schema definition

We establish the structure of components by creating a JSON Schema for them, defining their component API in code.

For an abridged version of that process, have a look at the TeaserCard again:

TeaserBox base component

This is the "full" TeaserBox component API / JSON Schema. We've just reduced the property definitions.

Select from available props

Let's highlight the ones we've identified when thinking about our component structure before.

Create your component API

Subsequently we add exactly those fields to our own components component API / JSON Schema...

Renaming props

... and we rename props as needed, in the process.

Add required fields

And finally, set all the fields identified as required.

Finished component JSON Schema

The finished component definition in all its glory.

@kickstartds/teaser-box.schema.json

_20
{
_20
"$schema": "http://json-schema.org/draft-07/schema#",
_20
"$id": "http://schema.kickstartds.com/base/teaser-box.schema.json",
_20
"title": "Teaser Box",
_20
"description": "Component to tease external content",
_20
"type": "object",
_20
"properties": {
_20
"image": {... },
_20
"ratio": { ... },
_20
"imageSpacing": { ... },
_20
"topic": { ... },
_20
"text": { ...},
_20
"inverted": {... },
_20
"link": { ... },
_20
"className": { ... },
_20
"component": { ... }
_20
},
_20
"required": ["ratio"],
_20
"additionalProperties": false
_20
}

Using multiple components

This process can also involve using properties from multiple components. For example when building one like the Storytelling component, using Headline, Button and RichText.

You can either just merge properties on your root component, or group included components on their own property. Like adding a link to something, which maps to the Button component and some of its properties (as their own object in your JSON Schema).

For the full version of creating the TeaserCard have a look at our guide "Create TeaserCard component".

React template

Now that our JSON Schema is defined, we'll automatically get matching TypeScript types for our component. We use those, combined with the types already included with the kickstartDS base component(s), to quickly hook up our set of properties to the mapped component(s). Using auto-complete, and TypeScript telling us about required properties in the mapped component(s), this is done in light speed!

To learn more about the tooling that create those types for you, and how to hook it up, see part four of our "Create your Design System" guide.

Let's continue showcasing this process using our TeaserCard, creating the component template:

Necessary imports

The main imports here are the kickstartDS base component(s) and our own components TypeScript types.

Add correct type to component

We need to type our React component to use our JSON Schema, while also making sure native HTML attributes are passed correctly. For the TeaserCard this means also including HTMLAttributes<HTMLElement> as it maps to a <div> under the hood.

Doing this allows users of your component to enjoy having the same auto-complete and safety when working with your Design System.

Add parameters to component

Next we add all our components defined properties to its function signature. For properties having a default defined in your component API we add that default here, too.

As we also want to pass through all the props not explicitly managed by us we sponge up ...props.

Add the actual JSX

For our component JSX we can directly use the TeaserBoxContextDefault, which is the kickstartDS base component.

Destructure additional props

Coming to our component JSX, we start by passing down (destructuring) the props we're carrying through first. This ensures properties defined in our component API will always take precedence, because they're added after the general props.

Glue component API to base component 1/3

The simplest cases of connecting props to the base component is when the name of the property is taken from the base component. We can just directly pass those without much thought.

Glue component API to base component 2/3

In some cases we might have changed the name of a prop. In that instance, we just to have to wire up the renamed property to the originally named base components property.

Glue component API to base component 3/3

Finally there might be fields where we're using a set of properties of the mapped component(s), but only expose one of those in our own component API... the rest get hard coded.

That's the case with our target property, which maps to the link.href property of the TeaserBox. We hard code the required label property to Read more.

Adding a Provider

The final part of creating our React component is adding a component Provider for it. As we've adapted a component here, we'll want to make sure that every time another component includes the base component, our own version of it gets used instead.

src/components/teaser-card/TeaserCardComponent.tsx

_35
import { HTMLAttributes, forwardRef, FC, PropsWithChildren } from "react";
_35
import {
_35
TeaserBoxContextDefault,
_35
TeaserBoxContext,
_35
} from "@kickstartds/base/lib/teaser-box";
_35
import { TeaserCardProps } from "./TeaserCardProps";
_35
_35
export const TeaserCard: FC<
_35
TeaserCardProps & HTMLAttributes<HTMLElement>
_35
> = ({
_35
headline,
_35
text,
_35
target,
_35
image,
_35
inverted,
_35
...props
_35
}) => {
_35
return (
_35
<TeaserBoxContextDefault
_35
{...props}
_35
topic={headline}
_35
text={text}
_35
link={{
_35
label: "Read more",
_35
href: target,
_35
}}
_35
image={image}
_35
inverted={inverted}
_35
/>
_35
);
_35
};
_35
_35
export const TeaserBoxProvider: FC<PropsWithChildren<any>> = (props) => (
_35
<TeaserBoxContext.Provider {...props} value={TeaserCard} />
_35
);

If you're wondering what that Provider we've added last is all about, think about it like this:
There may be other components you've built, that themselves use the base Button component by default. For example we might have our own TeaserCard component, based on the kickstartDS TeaserBox which includes a Button.

As a means to not having to go through every combination of those component now, making sure our Button actually gets used, you can just change the default Button rendered by adding a single Provider once, instead.

Learn more about Providers and React Context in our dedicated page about them.
Or look at our "Create your Design System" guide, where we add the general setup for Providers. That one also includes some more details on this!

Visual Studio Code component property quick-fix

Visual Studio Code has a great feature aiding in this workflow, with React components that include TypeScript types... like kickstartDS components do. When adding a "bare" component without props to your template, Visual Studio Code will offer you the option to Add missing attributes.

This will automatically create all required options for your component. Now you just have to connect your own props to those, while hard-coding the ones you don't plan on exposing as part of your components component API.

Just hover the squiggly, red line that should be decorating your component, and choose Quick Fix..., to get to that option (alternatively put your cursor on the component tag and hit Ctrl+.).

Technical debt added

This way of creating components adds minimal technical debt to your Design System. Not much has been changed around, we just add a small layer on top of the original kickstartDS base component(s).

Relevant underlying changes you'll have to look out for:

  • changes to the base components component API
  • removal of the base component(s)

You're immune to underlying changes to:

  • the components template (both React, and the resulting HTML)
  • the design and layout (changes to CSS, SCSS and Design & Component Token)

In the case of a changed component API, you should have a look at the corresponding CHANGELOG.md and potential notes in our matching migration guide. You'll probably just need to add a newly added field to your React template, and potentially your own component API if you want to use it. If a field was changed, that might also necessitate some adaption of your own version. Finally a removed field you're actually using would mean adding additional customization to regain that functionality. Have a look at our Headline example guide to see how you'd add your own, new property!

Learn more about what we mean by technical debt here on the overview page of this section.

Examples

We continuously expand our component example guides, below we've collected the ones acting as a good sample of the adaptation process.

Create TeaserCard component

In this example component guide we create the TeaserCard component, mapped to the TeaserBox component (part of the @kickstartDS/base module), to tease content in our Design System. We greatly simplify the Button to a single string props, and drastically reduce the rest of the options used, and offered, in our own adaptation of the TeaserBox.

This is what the result looks like:

Create Interstitial component

In this example component guide we create the Interstitial component, mapped to the Storytelling component (part of the @kickstartDS/content module), to add interstitial content elements that break the flow of content for really emphasized call-to-actions. As with all examples, we reduce the set of options greatly.

This is what the result looks like:


_10