Skip to main content

Customize a component

Overview

Customizing a component should be part of your arsenal if you already have a specific kickstartDS component in mind, but it's missing a certain feature that you require. Instead of starting from zero, you can always just customize existing base components for your own purposes.

It involves the same rough steps as adapting a component, but differs from it when it comes to creating the React template. Instead of (only) using the base component directly as imported, and just re-wiring properties, we customize the markup the base component uses to include our own behaviour. Everything else will basically be the same.

Customizing vs Extending

In both processes we add new features to a component, which results in it fitting one of the use cases we have in mind for our Design System.

In the case of customizing a component, we either change the way a property of an existing kickstartDS base component behaves, or we add a new property to it altogether. But it's always a change that results in customizing the React component template, too. For example: adding a switchOrder property to the Headline.

When extending a component, we take one existing kickstartDS base component and compose it with other kickstartDS base components to gain new functionality. We don't change the React component template in the process. For example: adding call-to-actions to our Section.

Both processes can be combined in a component, if needed!

Additionally this can of course also be used while creating a component, (see our TeaserCard component example for that). Or be used while extending a component that expands 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.

Customization process

As with the adaptation process, the big assumption here is that you already have a kickstartDS base component in mind. But unlike in that one, we have some requirement or feature that is not covered by it. We almost have a match. Additionally, the change required involves changing the component itself... not adding other kickstartDS base components into the mix. That would be covered by the extension process, instead.

Finding your component definition is still very simplified, we just have to add those properties not covered by the base component into the mix ourselves.

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 a base component. We'll also skip over, or significantly shorten, parts already covered by that guide. If unsure about something, best cross-reference it!

If you don't have a kickstartDS base component in mind yet, you're probably better served by our guide "Create a component". In that guide, you'll start off without a specific component in mind. This means defining a structure (someting you probably have an idea about already) first, and then mapping that structure to a fitting component second.

We also have an example for this with "Create Teaser Card component".

If you're still unsure, then maybe you're still missing a clear picture on what your components should look like. In that case, 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.

There are two main steps in customizing a component:

  1. Component definition, and
  2. Component creation

Let's get right to it!

1. Component Definition

The customizing process starts by defining a component API. As with the adaptation process this mainly means selecting a set of props from the pool of props available through your selected base component. But this time, we'll also add a requirement that's not covered by the base component.

We'll use the Headline component throughout this guide to illustrate concepts. This will not be an exhaustive example, though. For that have a look at our guide "Customize Headline component".

As our additional requirement, we'll want to be able to switch the order of the main und sub headlines. That's not something the base Headline can do, so we'll add this ourselves. We also want to be able to render some light RTE-like markup in our headline and subheadline, to emphasize something with by making it bold for example.

Purpose

There's not a lot different here when compared to the adaptation process. We just mainly keep close to the original purpose of our chosen kickstartDS base component.

There should be an exception though, as we have a requirement on our mind that is not part of the component yet. This will probably be part of the purpose of our component.

For our Headline example, this could mean adding more flexibility being that requirement. We might want our component to fit more use cases, by switching said order in specific circumstances, and allowing additional code formatting for its content.

Structure

We'll also still start by defining a rough draft of our component API here.

Let's keep going with our Headline component as an example. We're starting with the following properties:

PropertyTypeDescription
levelenumSelect the headline level to use, or p alternatively
styleAsenumSelect the headline style to use
alignenumChoose an alignment for the headline
content *stringText content for the headline
subheadlinestringText content for the optional subheadline
spaceAfterenumAdd additional spacing to the bottom of the headline
classNamestringAdditional Classes
componentstringOptional custom component identifier

Fields that should be required are marked with a *.

For the detailed documentation have a look at the Headline in our Storybook here:
https://www.kickstartds.com/storybook/?path=/docs/base-headline--h-1

One potential set of props, it's also the one used in our guide "Customize Headline component" if you're wondering, would be the following:

PropertyTypeDescription
text *stringText content of headline
substringSubheadline content
switchOrderbooleanSwitch order of headline and subheadline
level *enumLevel of headline to use
styleenumStyle of headline to show
spaceAfterenumWhether to display space after headline

We took level and spaceAfter directly, and renamed subheadline to sub, styleAs to style and content to text for our version of the Headline. And crucially we add our own property switchOrder into the mix. Required fields are marked with a * again.

2. Component Creation

In the second 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. While creating that template, we'll add the markup changes necessary to fit our additional requirement into the component.

JSON Schema definition

We establish the structure of components by creating a JSON Schema for them, defining their component API in code. This time we add our own, new property into the mix.

For an abridged version of that process, have a look at the Headline again (adding our own switchOrder property):

Original Headline

This is the "full" headline 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 rename props in the process.

Add our new prop

Most importantly, we add our new property.

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/headline.schema.json

_18
{
_18
"$schema": "http://json-schema.org/draft-07/schema#",
_18
"$id": "http://schema.kickstartds.com/base/headline.schema.json",
_18
"title": "Headline",
_18
"description": "Headline",
_18
"type": "object",
_18
"properties": {
_18
"level": { ... },
_18
"styleAs": { ... },
_18
"align": { ... },
_18
"content": { ... },
_18
"subheadline": { ... },
_18
"spaceAfter": { ... },
_18
"className": { ... },
_18
"component": { ... }
_18
},
_18
"required": ["content"]
_18
}

Customizing multiple properties

This process can also involve customizing a component by adding multiple properties to it. Just repeat all the steps for every property you'll need to add.

In general: these guides are mainly meant as basic building blocks, that can be combined and remixed by you to create exactly what you need... even if that's something more complex than described in these basic guides here.

For the full version of customizing a Headline have a look at our "Customize Headline component" guide.

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, to quickly hook up our set of properties to the original component. Using auto-complete, and TypeScript telling us about required properties in the base component, this is gets easy like squeezing a lemon!

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 Headline, creating the component template:

Necessary imports

The main imports here are the kickstartDS base component, a rendering function being capable of rendering Markdown and our own components TypeScript types.

Unlike the adapt, create and extend processes we don't actually need the HeadlineContextDefault, because we'll add our own markup... based on the original kickstartDS base components markup.

Defining RenderFunctions

We define the render functions interface that will enable our RTE-like functionality for the text and sub properties.

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 and our render functions are understood. For the Headline this means including HTMLAttributes<HTMLElement>, as it maps to a <div> under the hood, and our RenderFunctions interface.

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.

And in the case of the Headline, we also include our rendering functions.

Add the actual JSX

Unlike when directly adapting or extending a kickstartDS base component, we can't use some imported component directly. We need to reference the template of the original component we're customizing, to copy the parts that need customizing.

For the Headline this means copying and customizing this template.

Destructure additional props

We start with the props we're carrying through first. We add those to the root <header> element, like in the original component.

Adjustments to copied template markup 1/3

This is where we actually customize the original components markup.

For the Headline we drop properties we don't use (e.g. className), add hard coded values for others required but not part of our component API (e.g. align) and rename the ones we've given another name to (e.g. styleAs vs style)

Adjustments to copied template markup 2/3

The real custommization lies in adding the subheadline / sub in two places, before and after the main headline content. We just add matching if clauses using switchOrder and !switchOrder respectively, to toggle the one being displayed.

Adjustments to copied template markup 3/3

We also ensure adding our rendering functions to headline content and subheadline.

Adding a Provider

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

src/components/headline/HeadlineComponent.tsx

_66
import classnames from "classnames";
_66
import { HTMLAttributes, FC, PropsWithChildren } from "react";
_66
_66
import { HeadlineContext } from '@kickstartds/base/lib/headline';
_66
import { defaultRenderFn } from '@kickstartds/core/lib/core';
_66
_66
import { HeadlineProps } from './HeadlineProps';
_66
_66
interface RenderFunctions {
_66
renderContent?: typeof defaultRenderFn;
_66
renderSubheadline?: typeof defaultRenderFn;
_66
}
_66
_66
export const Headline: FC<
_66
HeadlineProps & RenderFunctions & HTMLAttributes<HTMLElement>
_66
> = ({
_66
text,
_66
sub,
_66
switchOrder = false,
_66
level = "h2",
_66
style = "h2",
_66
spaceAfter = "small",
_66
renderContent = defaultRenderFn,
_66
renderSubheadline = defaultRenderFn,
_66
...props
_66
}) => {
_66
const TagName = level;
_66
_66
return (
_66
_66
<>
_66
{text || sub ? (
_66
<>
_66
<header
_66
className={classnames(
_66
'c-headline',
_66
`c-headline--align-left`,
_66
spaceAfter && `c-headline--space-after-${spaceAfter}`
_66
)}
_66
{...props}
_66
>
_66
{sub && switchOrder && (
_66
<p className="c-headline__subheadline">{renderSubheadline(sub)}</p>
_66
)}
_66
<TagName
_66
className={classnames(
_66
'c-headline__headline',
_66
style !== 'none' && style !== level && `c-headline__${style}`
_66
)}
_66
>
_66
{renderContent(text)}
_66
</TagName>
_66
{sub && !switchOrder && (
_66
<p className="c-headline__subheadline">{renderSubheadline(sub)}</p>
_66
)}
_66
</header>
_66
</>
_66
) : (
_66
''
_66
)}
_66
</>
_66
); };
_66
_66
export const HeadlineProvider: FC<PropsWithChildren<any>> = (props) => (
_66
<HeadlineContext.Provider {...props} value={Headline} />
_66
);

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 Headline component by default. For example we might have our own TeaserCard component, based on the kickstartDS TeaserBox which includes a Headline.

As a means to not having to go through every combination of those component now, making sure our customized Headline actually gets used, you can just change the default Headline 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 a bit more technical debt than, for example, directly adapting or creating a component. Even extending a component can potentially carry less technical debt, if the extended component is mainly composed of unchanged kickstartDS base components. We still only add a small layer on top of the original kickstartDS base component from a component API perspective, but we also customize the markup.

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

  • changes to the base components component API
  • changes to the base components React template
  • removal of the base components

You're immune to underlying changes to:

  • 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!

Changes to the markup of a components React component will also need to be adapted. Those will always be Breaking Changes, though. And there'll always be a migration guide detailing the changes you'll have to follow. But it's still one crucial dependency added for you to worry about.

Immunity to underlying design and layout changes

How immune you are to changes to design and layout of a kickstartDS base component can depend on the changes you've made to the component while customizing. If you're closely adhering to the original CSS / SCSS BEM class structure, only hard coding some classes in your copied template, you should be safe.

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.

Customize Headline component

In this example component guide we customize the Headline component (as part of the @kickstartDS/base module) to use it for headlines in our own Design System. We greatly simplify the Headline and add both our own switchOrder property and RTE-like rendering capabilities for text content.

This is what the result looks like:


_10