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>
);
}