Creating Indepenent Bit Components in a NextJS Project

ig
ignacioaldama1 year ago

In an increasingly component-driven development world, a well-organized, efficient approach to managing these independent parts is key.

Bit provides a powerful, open-source solution for creating, sharing, and managing these components. Here, we'll explore how to leverage Bit's capabilities in a Next.js project, fostering better component isolation and modularity.

Getting Started

Initialize a Bit workspace

Initialize Bit at the root of your NextJS project (replace learnbit-react.rocket-roster with your own scope name):

$bit
Copiedcopy

Create a React component development environment ('env')

Run the following to create an env (component) for your React components. The env will provide your independent React components with all the tooling and configurations they need to be developed, built and delivered, independent of your project's setup.

$bit
Copiedcopy

The output should confirm that the env was created successfully, and provide you with the component ID of the env you just created. It should look something like this:

learnbit-react.rocket-roster/envs/react-env location: rocket-roster/envs/react-env env: teambit.envs/env (set by template)

Configure this env as the default env for future React components in your workspace:

/* @filename: workspace.jsonc */

{
  "teambit.generator/generator": {
    "envs": [
      /* replace the following with your env's component id */
      "learnbit-react.rocket-roster/envs/react-env"
    ]
  }
}
CopiedCopy

Watch for changes in the node_modules directory

Bit generates a corresponding Node package for each component you create. A component's package, generated in the node_modules of your project, is used to consume that component.

During the components' build a similar package is generated and persisted in the component snap (version). This package is then used to consume the component in other projects, as well. More on that in the following sections.

Set up NextJS to watch for changes (new compiled code) in the relevant directory, nested inside the node_modules folder. Add the following to your next.config.js file (replace learnbit-react with your own Bit account name):

/* @filename: next.config.js */

/** @type {import('next').NextConfig} */
const nextConfig = {
  webpack: (config) => {
    /**
     * add your Bit account name to the following regex.
     * replace the existing 'learnbit-react' account/org name
     * */
    config.snapshot.managedPaths = [
      /^(?!.*[\\/]node_modules[\\/](?:@learnbit-react)[\\/])(.+[\\/]node_modules[\\/])/,
    ];
    return config;
  },
  // ... other NextJS config
};

module.exports = nextConfig;
CopiedCopy

Create a React component

Run the following to create a React component:

$bit
Copiedcopy
React component or a NextJS component?

In this tutorial, we create a generic React component, not a NextJS component. This is because we want to create a component that is not dependent on NextJS, and can be used in any React project.

Having said that, some cases might require you to set your component as a NextJS component, to enable NextJS-specific features, and to communicate to other developers that a component is meant to be used in a NextJS project.

More on that in future tutorials.

Compile components

Run the following to compile your Bit components on every component change:

$bit
Copiedcopy

Bit React components will be compiled using the compiler configured by the React env you created in the previous step.

Use the component in your NextJS project

As you recall from an earlier section, Bit components are always consumed using their Node package, even when their source files are available in the workspace. In the example below, the previously created card component is consumed in the app's default page.tsx file:

/* @filename: src/app/page.tsx */

import { client } from './utils/client';
import Link from 'next/link';
/**
 * this is the previously created Bit components.
 * it is consumed using its node package, which was generated by bit.
 */
import { Card } from '@learnbit-react/rocket-roster.ui.card';

export default async function Home() {
  const allLaunches = await client.getUpcomingLaunches({});

  return (
    <main>
    // ...
          <h2 >
            Space Rocket upcoming launches
          </h2>
          <div>
            {allLaunches.results.map((launch) => (
              <Card key={launch.id} launch={launch} />
            ))}
          </div>
        </section>
        <Link
          href="/past"
        >
          View Past Launches
        </Link>
      </div>
    </main>
  );
}
CopiedCopy

To learn how to install Bit component in your workspace, See Installing components.

To learn how to import Bit component, to change and update them, see Importing components.

Previewing components

Run the following ro run Bit's dev server/Workspace UI:

$bit
Copiedcopy

The component previews are loaded from the (component's) *.composition.tsx files.

Make sure to set up your component compositions in a way that allows you to preview them in the Bit UI, without depending on NextJS. This will also make the component usable in other frameworks and projects.

For example, the card component is rendered without NextJS's image optimization, when previewed in the Bit UI:

/* @filename: {COMPONENT_DIRECTORY}/card.tsx */

/**
 * This component utilizes the image optimization feature, provided by NextJS.
 */
import Image from 'next/image';

export function Card({ launch, isPreview }: CardProps) {
  return (
    <article key={launch.id}>
      <header>
        <h5>{launch.name}</h5>
        <div>
          <Image
            src={launch.image}
            alt={launch.name}
            fill
            // disable NextJS image optimization in component previews
            unoptimized={isPreview}
          />
        </div>
        <Countdown date={launch.window_start} />
      </header>
        // ...
    </article>
  );
}
CopiedCopy

When the component is used in its .composition.tsx file, for previewing, the image optimization is turned off:

/* @filename: {COMPONENT_DIRECTORY}/card.composition.tsx */

export const BasicCard = () => {
  const [launch, setLaunch] = useState<any | null>(null);

  useEffect(() => {
    // ...
  }, []);

  // disable NextJS image optimization in component previews using the 'isPreview' prop
  return <>{launch && <Card launch={launch} isPreview />}</>;
};
CopiedCopy
Using NextJS features in component previews

Asx mentioned earlier, an alternative to disabling NextJS features in your component compositions, is to set your component as a NextJS component, using a dedicated NextJS component development environment. This will allow you to use NextJS features in your component compositions, and will also communicate to other developers that the component is meant to be used in a NextJS project.

Note that once your component is snapped and exported, the previews will be available in its component page, in the remote scope.

Managing dependencies

It is highly recommended to use Bit's dependency management feature to manage your components' dependencies. This is done by installing your project's (and Bit components') dependencies using Bit's install command.

$bit
Copiedcopy

The installation process does a few critical things:

  1. It reads the dependencies from the fallowing sources, and installs them in your project's node_modules directory:
    1. Your project's package.json file.
    2. Dependencies listed in the workspace's workspace.jsonc file, and in the various env.jsonc files, that are part of your component development environments.
    3. Your components' specific dependencies, listed in their snaps
  2. It compiles your components and creates a symlinks to them in your project's node_modules directory.

To learn how to use your own package manager, see 'configuring bit.cloud in your NPM config'.

Snapping and exporting components

When your component is ready to be shared, snap and export and Export components it to its a remote scope.