Organizing css-in-js Components with Namespaces

2024, Nov 15    

Introduction

For styling React applications, I’ve found Emotion to be an excellent choice. It offers an API similar to styled-components but with additional powerful features. In my daily workflow, I use a subset of its capabilities, yet it significantly improves maintainability.

While working on print graphics, I needed a method to manage multiple design versions efficiently. Here’s the structured approach I adopted.


Using Objects as Namespaces in css-in-js

One effective way to organize styled components is by grouping them under objects, treating them as namespaces. Here’s a structured Badge component:

const Badge = {
  structure: {
    Wrapper: styled.article`...`,
    Header: styled.header`...`,
    Body: styled.main`...`,
    Footer: styled.footer`...`,
  },
  elements: {
    Logo: styled.img`...`,
    Name: styled.span`...`,
    Company: styled.span`...`,
    Twitter: styled.span`...`,
    Type: styled.span`...`,
  },
};

By distinguishing between structure and elements, we achieve better separation of concerns, improving component reusability.

Implementing a Styled Badge Component

Now, we use the Badge object within a React component:

function StyledBadge({ logoUrl, company, name, twitter, type }) {
  return (
    <Badge.structure.Wrapper>
      <Badge.structure.Header>
        <Badge.elements.Logo src={logoUrl} />
      </Badge.structure.Header>
      <Badge.structure.Body>
        <Badge.elements.Name>{name}</Badge.elements.Name>
        <Badge.elements.Company>{company}</Badge.elements.Company>
        <Badge.elements.Twitter>{twitter}</Badge.elements.Twitter>
      </Badge.structure.Body>
      <Badge.structure.Footer>
        <Badge.elements.Type>{type}</Badge.elements.Type>
      </Badge.structure.Footer>
    </Badge.structure.Wrapper>
  );
}

This structured approach keeps components modular, making adjustments and extensions more intuitive.


Adapting Variants with Minimal Changes

If you’re organizing another conference with a different badge design, modifying the layout becomes straightforward:

function AlternativeStyledBadge({ logoUrl, name, type }) {
  return (
    <Badge.structure.Wrapper>
      <Badge.structure.Header>
        <Badge.elements.Logo src={logoUrl} />
      </Badge.structure.Header>
      <Badge.structure.Body>
        <Badge.elements.Name>{name}</Badge.elements.Name>
      </Badge.structure.Body>
      <Badge.structure.Footer>
        <Badge.elements.Type>{type}</Badge.elements.Type>
      </Badge.structure.Footer>
    </Badge.structure.Wrapper>
  );
}

Modifying Styles with Merging

For stylistic variations, we can extend the Badge while preserving its structure:

const AlternativeBadge = merge({}, Badge, {
  elements: {
    Name: styled(Badge.elements.Name)`...`,
    Type: styled(Badge.elements.Type)`...`,
  },
});

This ensures existing styles remain intact while introducing new variations where needed.


Enhancing Readability with Dynamic JSX

While the above structure improves maintainability, JSX can become verbose. A helper function, such as create, can streamline component rendering:

function AlternativeStyledBadge({ logoUrl, name, type }) {
  return create(AlternativeBadge)({
    Wrapper: {
      Header: { elements: ["Logo", logoUrl] },
      Body: { elements: ["Name", name] },
      Footer: { elements: ["Type", type] },
    },
  });
}

This abstracts the JSX complexity, making it easier to manage and scale.


Conclusion

While this technique introduces some verbosity, it significantly improves component modularity and reusability. The structured approach avoids polluting namespaces while maintaining a clear hierarchy.

This method can be further enhanced by integrating with tools like Styled System, allowing developers to build more efficient and scalable styling solutions.