Jumpstart your Angular 14 development with Storybook, Tailwind and Nx

Colum Ferry - Jun 28 '22 - - Dev Community

With Angular 14 having just released recently, let us discuss how you can take advantage of some of the most popular tools in the JavaScript ecosystem to increase your efficiency and productivity when building Angular applications.

In this article, we will setup an Nx workspace that is preconfigured for Angular development, and integrate Storybook and Tailwind using generators provided by Nx. We will then build a simple Button component using only Storybook and Tailwind.

Workspace Setup

First, let’s start by creating a new Nx Workspace that contains the official Angular plugin. It’s as simple as running a single command:

npx create-nx-workspace@latest demo --preset=angular

We will then be prompted to answer questions about what we’re creating.
Give the application a name of myapp and then select the default answers for the rest of the prompts.

This will create our workspace and install the dependencies required to build Angular applications! Ensure you're at the root of the new workspace by running cd demo.

We want to build a component that will be shared among many applications, so let’s create a Shared Components library to store it. This allows us to add more components in the future and keep them scoped to a shared domain. Run the following command

npx nx g @nrwl/angular:lib shared/components

This will create a workspace library with a name of shared-components but with a directory structure of shared/components.

This can be beneficial for domain driven development and is a good architectural pattern to follow when working within an Nx Workspace.

Add Storybook

Now, let’s add Storybook to our workspace, and more importantly, to our shared-components library. This is as simple as running the following command:

npx nx g storybook-configuration shared-components

Say No to all the prompts that are asked in the terminal.

This will install Storybook, create the necessary Storybook configuration files and add two targets to the shared-components project.json file, enabling us to run Storybook for the library.

Add Tailwind

Next, we want to add Tailwind support to our workspace. Again, Nx offers a command to do this for us! Just run:

npx nx g setup-tailwind myapp

This will add the Tailwind directives to our styles.css file in apps/myapp/src/style.css and create a tailwind.config.js file for our application.

However, Storybook will not be aware of Tailwind for our shared-components library. To allow this, we need to do the following:

  1. Add apps/myapp/src/styles.css to styles array of build-storybook
  2. Copy and Paste tailwind.config.js from myapp to libs/shared/components.

Note: We need to copy and paste the tailwind.config.js file from the application for our UI Library to allow Storybook to find the Tailwind classes correctly.
This is not the most scalable solution to this problem as there will be a duplication of configuration across all the libraries in the workspace. Changing the root configuration will not edit all the other libraries’ configurations.
You can find a better approach to solving this problem in Leosvel’s blog post: https://leosvel.dev/blog/set-up-tailwind-css-with-angular-in-an-nx-workspace/#sharing-the-tailwind-css-configuration-between-the-application-and-the-buildable-library

And that's it! We're now ready to start building components independently of our application using Storybook!

Let's build a Button component

The first thing we want to do is generate a component to store our button. We'll take advantage of the SCAM pattern and use Nx's SCAM generator. Run the following command:

npx nx g @nrwl/angular:scam button --inlineTemplate --inlineStyle --export --project=shared-components
Enter fullscreen mode Exit fullscreen mode

This will generate an NgModule, a Component and use inline template and inline styles (also following the Single File Component pattern) within our shared-components library and export it to be consumed by other libraries or applications.

A new file should be generated at libs/shared/components/src/lib/button/button.component.ts with the following contents:

import { Component, OnInit, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'demo-button',
  template: ` <p>button works!</p> `,
  styles: [],
})
export class ButtonComponent implements OnInit {
  constructor() {}

  ngOnInit(): void {}
}

@NgModule({
  imports: [CommonModule],
  declarations: [ButtonComponent],
  exports: [ButtonComponent],
})
export class ButtonComponentModule {}
Enter fullscreen mode Exit fullscreen mode

It's a pretty straightforward component! Let's edit the template slightly to:

  • Use Tailwind for styling
  • Allow content projection

Replace the contents of template: with:

<button
   class="
      inline-flex
      items-center
      justify-center
      px-5
      py-5
      border
      border-transparent
      text-base
      font-medium
      rounded-md
      text-white
      bg-indigo-600
      hover:bg-indigo-700
      "
>
  <ng-content></ng-content>
</button>
Enter fullscreen mode Exit fullscreen mode

Now, we can automatically generate a Storybook story for this component, allowing us to see and design this component independently from consuming applications. We can do this by running the following command:

npx nx g @nrwl/angular:stories shared-components --generateCypressSpecs=false
Enter fullscreen mode Exit fullscreen mode

This will create a new file libs/shared/components/src/lib/button/button.component.stories.ts with a Storybook story automatically created!

Next, if you run npx nx storybook shared-components, you can see in the terminal that Storybook will run and create an instance on http://localhost:4400/. If you navigate to that URL, you will see Storybook is running correctly!

Storybook rendering button

Our button doesn't have any text because it uses content projection. We can solve this by making a few quick edits to our story.

We'll set up a "Harness" component, that will be scoped to just the story and is used to host and render our actual ButtonComponent. This pattern allows us to set up a Storybook Control that will allow us to easily change the text that is rendered within the button!

Replace the contents of libs/shared/components/src/lib/button/button.component.stories.ts with

import { moduleMetadata, Story, Meta } from '@storybook/angular';
import { Component, Input } from '@angular/core';
import { ButtonComponent } from './button.component';

@Component({
  selector: 'demo-button-harness',
  template: '<demo-button>{{ text }}</demo-button>'
})
class ButtonHarnessComponent {
    @Input() text = 'Button';
}

export default {
  title: 'ButtonComponent',
  component: ButtonHarnessComponent,
  decorators: [
    moduleMetadata({
      imports: [],
      declarations: [ButtonComponent]
    })
  ],
} as Meta<ButtonHarnessComponent>;

const Template: Story<ButtonHarnessComponent> = (args: ButtonHarnessComponent) => ({
  props: args,
});


export const Primary = Template.bind({});
Primary.args = {
    text: 'Button'
}
Enter fullscreen mode Exit fullscreen mode

You should then see the following in Storybook:

Button with control in Storybook

And that's it! How easy is that!?

Nx truly helps you to hit the ground running with Angular development thanks to all of it's one-command integrations!

Recap

  1. With Nx, we were able to create a workspace with a structure that allows for a better separation of domains.
    1. npx create-nx-workspace@latest demo --preset=angular
  2. Nx also offered us commands to easily integrate modern tooling allowing us to develop rapidly.
    1. npx nx g storybook-configuration
    2. npx nx g setup-tailwind
  3. Nx offered some commands to help us scaffold out code making us even more productive, while sticking to consistent structures and patterns.
    1. npx nx g @nrwl/angular:lib
    2. npx nx g @nrwl/angular:scam
  4. Finally, Nx reuses a CLI API we are familiar with to interact with our app and libraries
    1. npx nx storybook shared-components

Migrating to Nx

Do you already have an Angular application using the Angular CLI and want to take advantage of all the cool tooling that Nx offers? Don't worry! The Nx CLI offers a single command that will automatically migrate most Angular workspaces to use Nx.

It has been recently refactored to support multi-project workspaces as well as some known standard deviations from Angular's opinionated workspace scaffolding.

You can use the command below in your Angular Workspace to kick off the migration.

ng add @nrwl/angular
Enter fullscreen mode Exit fullscreen mode

Note: You need to ensure you use the correct command based on the version of Angular your workspace is using. The easiest way would be to ensure your Angular workspace is at the latest Angular version and then run the command!

Otherwise, you can read more about migrating to Nx here, including the command to run based on your version of Angular: https://nx.dev/migration/migration-angular

Conclusion

Hopefully you can see just how easy it can be to integrate tooling and build out applications and component libraries using modern tooling such as Storybook and Tailwind with Nx!

We would love for you to try it out and let us know what you think!

Also, make sure you don’t miss anything by

Following us on Twitter, and
Subscribe to the YouTube Channel for more information on Angular, React, Nx, and more!
Subscribing to our newsletter!
As always, if you are looking for enterprise consulting, training and support, you can find out more about how we work with our clients here.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .