Building Our Extremely Lite styled-components

styled-components is great when it comes to css-in-js solution. Now we will be doing extremely basic styled-components implementation to understand how it works.

Letโ€™s start!

An example styled-components usage is like below;

const StyledButton = styled.button`
  color: pink;
`;


As you see styled-components using template literals to apply styles.

Template literals allow us to call functions with backticks. Letโ€™s check what i mean below: For more: tagged-template-literals

const boo = (strings: TemplateStringsArray) => {
  return strings[0];
};

boo`hello` // output: hello


Our first implementation is below. We have tag param which is our html tag, strings is used for template-literal styles and finally react element is created based on our tag & template-literal strings.

We are also using css2obj helper function to convert css strings into javascript object.

const styled = (tag) => {
  return (strings) =>
    ({ children }) => {
      const cleanCss = strings[0].replace("\n", "");
      const objCss = css2obj(cleanCss);

      return React.createElement(tag, { style: { ...objCss } }, children);
    };
};

const StyledButton = styled("button")`
  color: pink;
`;

This works fine! But i donโ€™t want to use html tags as string here.



So i changed code a bit. Now we seperated component generation into generateComponent function and looped through some predefined html tags to ensure type safety.

const tags: Tag[] = ["div", "button", "span", "header"];

function generateComponent(tag: Tag) {
  return (strings: TemplateStringsArray) =>
    ({ children }: StyledComponent) => {
      const cleanCss = strings[0].replace("\n", "");
      const objCss = css2obj(cleanCss);

      return React.createElement(tag, { style: { ...objCss } }, children);
    };
}

 const styled = Object.fromEntries(
  tags.map((tag) => [tag, generateComponent(tag)]),
)

Now we can use it like below:

const StyledButton = styled.button`
  border: none;
  border-radius: 4px;
  background-color: pink;
  color: #111;
  padding: 3px 10px;
  cursor: pointer;
`;

<StyledButton>๐Ÿ’…๐Ÿป Hello</StyledButton>;




Full Code ๐Ÿ‘‡๐Ÿผ

import React from "react";
import type { DetailedReactHTMLElement } from "react";

type StyledComponent = {
  children: React.ReactNode;
};

type Tag = keyof JSX.IntrinsicElements;

type Styled = Record<
  Tag,
  (
    strings: TemplateStringsArray,
  ) => (props: StyledComponent) => DetailedReactHTMLElement<any, HTMLElement>
>;

const tags: Tag[] = ["div", "button", "span", "header"];

function generateComponent(tag: Tag) {
  return (strings: TemplateStringsArray) =>
    ({ children }: StyledComponent) => {
      const cleanCss = strings[0].replace("\n", "");
      const objCss = css2obj(cleanCss);

      return React.createElement(tag, { style: { ...objCss } }, children);
    };
}

export const styled = Object.fromEntries(
  tags.map((tag) => [tag, generateComponent(tag)]),
) as Styled;

const css2obj = (css: string) => {
  const r = /(?<=^|;)\s*([^:]+)\s*:\s*([^;]+)\s*/g,
    o: Record<string, string> = {};

  css.replace(r, (m, p, v) => (o[p] = v));
  return o;
};

Back to Home