Dynamic configurable text components with styled-components

 • 

Up until recently when working with styled-components in my React Native apps I defined all my text components for every needed configuration in a single file like so:

import styled from "styled-components";
import COLORS from "../../global/colors";


/*
100    Extra Light or Ultra Light
200    Light or Thin
300    Book or Demi
400    Normal or Regular
500    Medium
600    Semibold, Demibold
700    Bold
800    Black, Extra Bold or Heavy
900    Extra Black, Fat, Poster or Ultra Black
*/

export const Roboto10 = styled.Text`
        fontFamily: Roboto-Regular;
        fontSize: 10;
        fontWeight: normal;
        fontStyle: normal;
        lineHeight: 11;
        letterSpacing: 0;
        color: ${COLORS.purpleBrown};
`;

export const Roboto14 = styled.Text`
        fontFamily: Roboto-Regular;
        fontSize: 14;
        fontWeight: normal;
        fontStyle: normal;
        lineHeight: 24;
        letterSpacing: 0;
        color: ${COLORS.purpleBrown}
`;

export const Roboto16 = styled.Text`
        fontFamily: Roboto-Regular;
        fontSize: 16;
        fontWeight: normal;
        fontStyle: normal;
        lineHeight: 24;
        letterSpacing: 0;
        color: ${COLORS.purpleBrown}
`;

export const Roboto16Bold = styled.Text`
        fontFamily: Roboto-Regular;
        fontSize: 16;
        fontWeight: bold;
        fontStyle: normal;
        lineHeight: 24;
        letterSpacing: 0;
        color: ${COLORS.purpleBrown};
`;

This way of doing it is quite cumbersome because:

  • you have to do a lot of writing.
  • you always have to check whether a component with a given configuration already exists or not (as to not add duplicates).
  • when you have to change the configuration, it might lead to unexpected UI changes in places you might not be aware of.

So recently I a started writing dynamically configurable text components.


/*
100    Extra Light or Ultra Light
200    Light or Thin
300    Book or Demi
400    Normal or Regular
500    Medium
600    Semibold, Demibold
700    Bold
800    Black, Extra Bold or Heavy
900    Extra Black, Fat, Poster or Ultra Black
*/

export type TextComponentConfig = {
    font: ?string,
    fontSize: ?number,
    lineHeight: ?number | ?string,
    fontWeight: ?number | string,
    fontStyle: ?number | string,
    color: ?string
}

const components = Object.create(null);

export default function TextComponents({
    font, fontSize, lineHeight, fontWeight, fontStyle,
}) {
    const key = `${font}_${fontSize}_${fontWeight}_${fontStyle}`;

    if (components[key]) {
        return components[key];
    }

    const TComp = styled.Text`
        fontFamily: ${font || ""};
        fontSize: ${fontSize || 16};
        fontWeight: ${fontWeight || "normal"};
        fontStyle: ${fontStyle || "normal"};
        lineHeight: ${lineHeight || fontSize || 16};
        letterSpacing: 0;
        color: ${COLORS.purpleBrown}
    `;
    components[key] = TComp;
    return TComp;
}

The TextComponents function takes an object that contains the configuration parameters for the desired text components. It then creates a key for that specific text component by simply stringing together all the passed parameter values into a key string. This key string is used to look up whether a component with that very same key has already been created and exists in the components object. If so, that component is returned. If not, we create a new component, save it in the components object and return it.

As you can see, I am not making use of all of the properties than can be used to style a Text component (for instance lineHeight, which should be trivial to add however.

Using TextComponents

The function can be used by passing the font configurations parameters as an object. Take a look at the following snippet to see how to dynamically generate and use a text component with the TextComponents function.

import React from "react";
import TextComponents from "./TextComponents";
const GreetingText = TextComponents({ font: "Oswald-Regular", fontSize: 18, color: COLORS.lightBlack, fontWeight: "bold", lineHeight: 25 });

export function HelloWorld(){
    return(
      <GreetingText>Hello World!</GreetingText>
    );
}