How To Make Text Component with React, TypeScript and Styled Components

July 1, 2023

4 min read

How To Make Text Component with React, TypeScript and Styled Components
Watch on YouTube

Text is likely the most used component in any app. Today I want to share a solid reusable Text component that is very useful in my front-end development work and should save time for you too.

Text Component

The Text component is just a styled paragraph that we can also turn into any other element like div, span, or heading by passing the as property. Also, we can modify it any way we like, using the styles property or wrapping it with the styled function. Because of that flexibility, it serves as a building block for other components.

import styled, { DefaultTheme, css } from "styled-components"

const getTextColorRecord = ({ colors }: DefaultTheme) =>
  ({
    regular: colors.text,
    supporting: colors.textSupporting,
    supporting2: colors.textSupporting2,
    supporting3: colors.textSupporting3,

    primary: colors.primary,
    attention: colors.attention,
    alert: colors.alert,
    success: colors.success,
    reversed: colors.background,
    contrast: colors.contrast,
  } as const)

type TextWeight = "regular" | "semibold" | "bold"
const fontWeight: Record<TextWeight, number> = {
  regular: 400,
  semibold: 500,
  bold: 600,
}

type TextHeight = "small" | "regular" | "large"
const lineHeight: Record<TextHeight, number> = {
  small: 1,
  regular: 1.2,
  large: 1.5,
}

export type TextColor = keyof ReturnType<typeof getTextColorRecord>

export interface TextProps {
  color?: TextColor
  weight?: TextWeight
  size?: number
  height?: TextHeight
  centered?: boolean
  cropped?: boolean
  nowrap?: boolean
}

export const oneRemInPx = 16

const getFontSize = (sizeInPx: number) => {
  const sizeInRem = sizeInPx / oneRemInPx

  return `${sizeInRem}rem`
}

export const Text = styled.p<TextProps>`
  margin: 0;
  padding: 0;
  overflow-wrap: break-word;

  ${({ color, theme }) =>
    color &&
    css`
      color: ${getTextColorRecord(theme)[color].toCssValue()};
    `}
  ${({ weight }) =>
    weight &&
    css`
      font-weight: ${fontWeight[weight]};
    `}
  ${({ height }) =>
    height &&
    css`
      line-height: ${lineHeight[height]};
    `}
  ${({ size }) =>
    size &&
    css`
      font-size: ${getFontSize(size)};
    `}
  ${({ centered }) =>
    centered &&
    css`
      text-align: center;
    `}

  ${({ nowrap }) =>
    nowrap &&
    css`
      white-space: nowrap;
    `}

  ${({ cropped }) =>
    cropped &&
    css`
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    `}
`

For UI consistency we reset default browser CSS at the beginning of the Text styling by removing margings and padding and setting overflow-wrap to break-word to prevent text overflow from breaking the layout. Then we continue by handling it's properties.

Let's look into extra properties that we can pass to the Text component. While we can set set color through styles or className, most often we will use the color property. It's a limited set of named colors, such as regular, supporting, or alert. We access all of them through the getTextColorRecord function that in turn relies on the styled-components theme. Note, that all the names describe the color rather that saying ellow or white. That way we can change the color palette and add new themes without changing the Text component.

The weight property turns regular, semibold and bold into 400, 500, 600. It's usually enough for most cases. But you can also extends it for you situation, andd thin or extrabold options for example.

The size is an interesting and opinionated property. While we pass a pixels as a number it's being converted to rems. For us it's easier to think in pixels. We think of 16px as a default size, and to make a smaller variant it's more intuitive to pass 14px rather than 0.875rem. At the same time we make the app more accessible by converting pixels to rems, so that users can change the font size in their browser settings and the app will adjust accordingly.

The height property represents line-height and can be small, regular, or large, yet we are rarely going to use it.

While some people prefer having variants for texts, from my experience I found it more practical to rely on properties like size and weight to create a variety of text styles. Yet, if your project would benefit from having variants, you can add it as a property, and then have a record of styles for each variant.

Finally there are centered, cropped, and nowrap helpers that are self-explanatory.