Forem

Cover image for From Specific to Reusable: Transforming UI Components with Bit
John Brooks
John Brooks

Posted on

From Specific to Reusable: Transforming UI Components with Bit

Before Bit became part of our workflow, sharing and collaborating on individual components felt like climbing a steep mountain. Managing dependencies, packaging, documentation, and setting up elaborate build tools wasn’t just time-consuming — it was frustrating. These setups often relied on third-party tools that were prone to issues, introducing bugs and bottlenecks along the way.

Because of these hurdles, sharing components was typically limited to the most generic and reusable ones, like UI elements or utility functions. Everything else—components tailored to specific use cases—was left isolated within teams, making collaboration a challenge.

But with Bit, things have changed. Today, developers embrace component-driven development, where every part of an application is built as loosely coupled, self-contained components. Bit doesn’t just help you share code — it transforms your codebase into a well-structured graph, where every node (a component) has a clear purpose and name.

An example of a composable project: readable, comprehensible and easy to maintain


A Tale of Two Teams: Turning Specific Components Into Reusable Gems

This blog explores how Bit helps teams refine specific components into reusable ones, as demonstrated by the collaborative journey of two teams: HR and CRM.

Chapter 1: The HR Team Builds the "Employee Card"

The HR team was tasked with creating an Employee Card to display employee details for their HR platform. They used foundational UI elements shared by the Design Team—buttons, typography, and other reusable building blocks.

The result? A highly effective, well-tested, specific component designed for a single purpose: displaying employee information.


Chapter 2: The CRM Team Discovers an Opportunity

Meanwhile, the CRM team needed a Customer Card for their CRM platform. While exploring the organization's components on *Bit Platform, they stumbled upon the Employee Card. It wasn’t a perfect match — some fields and functionality didn’t align with customer needs—but it came close enough to spark an idea.

Instead of starting from scratch, the CRM team saw potential in refining the specific Employee Card into something more reusable.

Image description

The employee card before being modified by the CRM team:

// imports...

export interface EmployeeCardProps {
  contractType: 'full-time' | 'part-time' | 'intern';
 // ...

}

const contractTypeColors = {
      'full-time': 'success',
      'part-time':'warning',
      intern: 'info'
  };

export const EmployeeCard = ({
  avatarUrl,
  // ...
}: EmployeeCardProps) => {
// ...

  return (
    <Card>
      // ...
      <CardContent>
        // ...
          <Chip
            label={`${contractType.toUpperCase()}`}
            color={contractTypeColors[contractType]}
          />
      // ...
    </Card>
  );
};
Enter fullscreen mode Exit fullscreen mode

Chapter 3: Generalizing the Employee Card

The CRM team proposed changes to generalize the Employee Card while keeping it backward compatible. The modifications included:

  • Customizable Sections: Allowing developers to replace default fields with custom ones, such as replacing the 'contract type' with online/offline status.

  • Backward Compatibility: Ensuring existing functionality for the HR platform remained intact.

By introducing a flexible API, the Employee Card became more than a specific component—it was now reusable across multiple contexts.

Here’s an example of the transformation:

// imports ...

const contractTypeColors = {
  'full-time': 'success',
  'part-time': 'warning',
  intern: 'info',
} as const;

type ContractType = typeof contractTypeColors;
/**
 * the custom color-map lables can be any string, 
 * but the color values can only be those supported by this component
 */ 
type ChipColorMap = Record<string, ContractType[keyof ContractType]>;

/**
 * Extend the prop type to allow for custom color maps and values
 * instead of the default `contractTypeColors`
 */
export interface EmployeeCardProps<T extends ChipColorMap = ContractType> {
  contractType: T extends ContractType ? keyof ContractType : keyof T;
 /**
  * Nest the props for cutomization under `options` as these are
  * expected to only be used by developers extending this component
  * (rather than using it directly)
  */ 
  options?: {
    chipColorMap: T;
  };
 // ...
}

export const EmployeeCard = <T extends ChipColorMap>({
  contractType,
  options,
 // ...
}: EmployeeCardProps<T>) => {
  /**
   * Use the custom color maps and values, if available
   */
  const chipColorMap = options?.chipColorMap ?? contractTypeColors;

  return (
    <Card>
      <CardContent>
          <Chip
            label={`${contractType.toUpperCase()}`}
            color={(chipColorMap as T)[contractType]}
          />
      // ...
      </CardContent>
    </Card>
  );
};
Enter fullscreen mode Exit fullscreen mode

Chapter 4: Building the Customer Card

The CRM team submits a Change Request (similar to a “Pull Request”) to the HR team via Bit Platform. This request includes detailed documentation of the changes and previews showcasing the card’s extended functionality.

# create a new 'lane' (analogous to git branches)
bit lane create enable-custom-chip-values

# create a 'snap' (analogous to git commit)
bit snap --message "enable the display of custom values instead of contract types" 

# push the 'snap' to Bit Platform and build a pre-release package version of it
bit export
Enter fullscreen mode Exit fullscreen mode

While waiting for the HR team to review their changes, the CRM team began building the Customer Card using the modified Employee Card as a base. Here’s what that looked like:

import {
  EmployeeCard,
  type EmployeeCardProps,
} from '@learnbit-react/generalization.employee-card';

const statusColors = {
  online: 'success',
  offline: 'warning',
} as const;

type Status = typeof statusColors;

export interface CustomerCardProps
  extends Omit<EmployeeCardProps<Status>, 'contractType'> {
  status: keyof Status;
}

export function CustomerCard({
  status,
 // ...
}: CustomerCardProps) {
  return (
    <EmployeeCard<Status>
      contractType={status}
      fullName={fullName}
      startDate={startDate}
      avatarUrl={avatarUrl}
      options={{
        chipColorMap: statusColors,
      }}
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

The “Customer Card” dependencies page (shown in Bit’s Workspace UI) reveals the new dependency:

This approach allowed the CRM team to work in parallel without disrupting the HR team’s workflow. Once the changes to the Employee Card were approved, the CRM team simply updated their dependency to the latest version, saving time and avoiding duplication.

For example:

# install using bit
bit install @acme/hr.employee-card@1.1.0

# install using npm
npm i @acme/hr.employee-card@1.1.0
Enter fullscreen mode Exit fullscreen mode

Benefits of Turning Specific into Reusable

The HR and CRM teams’ collaboration showcases the power of Bit to transform specific components into reusable assets. Here are the key benefits:

  • Faster Development: Teams can build new components by extending existing ones.
  • Consistency: Reusing components ensures a unified design language across the organization.
  • Easier Maintenance: Updates to shared components propagate automatically to all their dependents.

When to Generalize (and When Not To)

Not every component needs to be generalized. If the differences between use cases are too significant, overgeneralizing can lead to:

  • Complexity: Components become harder to maintain and understand.
  • Bug Risks: Extending functionality increases the chance of introducing errors.

In these cases, it might be better to create a new component by forking an existing one or starting fresh.

For example:

# Use an existing shared component as the template for a new component
bit fork COMPONENT_ID
Enter fullscreen mode Exit fullscreen mode

Conclusion

The process of turning specific components into reusable ones with Bit is more than just a workflow—it’s a mindset. It empowers teams to collaborate seamlessly, build more efficiently, and maintain high-quality software.

By striking the right balance between specificity and reusability, you can transform your codebase into a flexible, maintainable system. Try Bit today and experience the difference in component-driven development firsthand!

Top comments (0)