Developing Scalable Frontends with Feature-Sliced Design (FSD)

Building modern frontends with Feature-Sliced Design and Bit

Lakindu Hewawasam
Bits and Pieces

--

If you’ve worked on large-scale frontend apps, chances are that you’ve run into issues where you can’t understand your project.

You’d likely have files and components created all over your project that rely on each other, which could make maintaining it a huge mess!

So, to avoid such issues, you would need a compilation of rules and conventions that show you the best way to organize your frontend app. And that’s exactly what Feature Sliced Design is all about.

Pst, if you directly want to dive into the code, check out my Bit Scope.

What is Feature Sliced Design?

Feature Sliced Design is a frontend architectural pattern that is used to scaffold frontend apps. Simply put, it’s a compilation of rules and conventions for organizing code.

Its main objective is to make the project more understandable and structured in the face of ever-changing business requirements.

It does this by breaking a frontend application down into three components:

If you’re building with Feature Sliced Design, your app will consist of these three components — Layers, Slices and Segments.

  1. Layers: Layers are standardized across all projects and are vertically arranged. This means that communication can happen from top to bottom. For example, the Pages layer can communicate with the Widgets layer, not the other way around. Additionally, your app can have up to 6 layers:
  2. shared — Reusable functionality, detached from the specifics of the project/business. (e.g. UIKit, libs, API)
  3. entities — Business entities. (e.g., User, Product, Order)
  4. features — User interactions, actions that bring business value to the user. (e.g. SendComment, AddToCart, UsersSearch)
  5. widgets — Compositional layer to combine entities and features into meaningful blocks. (e.g. IssuesList, UserProfile)
  6. pages — Compositional layer to construct full pages from entities, features and widgets.
  7. app — App-wide settings, styles and providers.
  8. Slices: Every layer consists of slices. These slices partition your code within a layer via the business domain. By doing so, this makes your code easier to navigate across and keeps logically related modules close together. However, it is important to note that slices cannot communicate with slices within the same layer, but only the layer below it.
  9. Segments: Each slice consists of segments. A Segment is a tiny module that helps seperate code within a slice based on its technical purpose. For example, you might have different segments for — ui, api, lib. Each segment stores code based on different technical purposes.

By structuring your code in such a manner, you introduce the following factors:

  1. Uniformity: Your frontend app now has a defined standard convention to adhere to based on layers, slices and segments.
  2. Domain Driven: Your app is organized to be more business driven rather than tech driven. This lets you navigate across your project easier and understand features more deeply.
  3. Better Maintainability: Since a module cannot communicate with a module on the same layer, or the layer above it, your app is less prone to breaking after refactoring.

Should I Use Feature Sliced Design?

Now, on the surface, it’s clearly evident that Feature Sliced Design takes a significant amount of effort to implement in a frontend app, regardless of whether you are starting from scratch or migrating to it.

Therefore, it’s important to understand that feature-sliced design is not for everyone. In fact, I recommend using Feature Sliced Design under these circumstances:

  1. You’re building a frontend app. Do not try to model a backend application using feature sliced design.
  2. You’re building a user facing application, and not a UI Library. UI libraries don’t have any business domains, nor API calls to handle. Only a User Facing application could be separated by a domain.
  3. You’re building a large scale project, and not a simple app. You might now realize the benefits of FSD if you’re building a simple Todo application. But, if you’re building an application like WriterGate or Medium, FSD can come in handy.

If your frontend app meets these three requirements, go ahead with FSD!

Building An App Using Feature Sliced Design and Bit

If you’ve made it this far, your app is likely a strong candidate to FSD. So, let’s take a look at how we can build an app using FSD!

I’ll be using Bit to build an application using FSD. Bit is a next-generation build system for composable software.

Bit lets you build independent components that are designed, developed, versioned in an isolated space and is hosted on a remote scope. These scopes promote logical structuring of your components through namespaces that lets you better visualize and maintain your apps easier:

As you can see, the scope shown above logically orders components in different namespaces — brand, elements, fonts. By leveraging these namespaces, you can streamline FSD with Bit at ease!

So, let’s start building!

Step 01: Pre-requisites

First, use Bit’s version manager (BVMM) to install Bit globally and then initialize a workspace with React. This will help us work on FSD with a React space.

# install bit
npx @teambit/bvm install

# initialize workspace
bit new react workspace --default-scope dummyorg.fsd --aspect teambit.react/react-env

Make sure to replace dummyorg.fsd with your Bit Username and Scope Name.

Note, workspaces aren’t tied to a language. You can build different components like Node.js, Angular, Next.js and React in a single workspace using Bit Environments.

If you’ve created your workspace successfully, you should see the output below:

Next, run bit start to launch the local server. You should see the output below:

Step 02: Defining the React app using Feature Sliced Design

Next, let’s start building a Feature Sliced Design driven React app using Bit Components. For this demo, let’s build a React app that fetches a list of blogs.

So, in our app, we’d have the following:

  1. A React App that holds the entire app together
  2. A Blog page that renders the blog items
  3. A Card component that renders a single blog item.
  4. An entity that represent a blog
  5. An API call that fetches a list of blogs.

If we structure this in FSD, we should have a logical mapping as follows:

  1. app: This directory will hold a React app component for the blog list.
  2. pages: This directory will hold a component for the blog-list-page.
  3. widgets: A single slice will be created that defines a blog-list
  4. features: Will define a slice called get-blog-posts as the first feature. In that slice, we'll define one segments - model that defines the data fetching mechanisms for the feature to work.
  5. entities: Will define a slice called blog. Inside the blog slice, there will be two segments - model and ui. The model will define the data shape for a Blog Item while the ui hold a React component called blog-card that defines a blog post card.

Step 03: Creating the Components with Bit

Next, let’s create all required components using Bit.

1: Building The Entity Layer

First, let’s build the Entity layer. To do so, let’s create the blog slice using the command:

bit create react-hook entities/blog/model && bit create react entities/blog/ui/blog-item

You should see new directories created as below:

With Bit, you’ve likely noticed that you get a spec.tsx file as well as a composition.tsx file. You're able to build with Test Driven Development by creating test cases for your single component. Additionally, you can use the compositions file to create different outputs for your component to show consumers how it can be used.

For simplicity, I won’t be writing test cases, so I’ll delete the test files from my components to keep this demo simple.

Next, let’s define the Blog model and the Blog Card UI by updating the model.ts and blog-item.tsx files as shown below:

// blog-item.tsx

import type { ReactNode } from 'react';
export type BlogItemProps = {
id: string,
title: string,
description: string
tags: string[]
};
const cardStyle: React.CSSProperties = {
border: '1px solid #ddd',
borderRadius: '8px',
padding: '16px',
margin: '16px',
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)',
backgroundColor: '#fff',
};
const titleStyle: React.CSSProperties = {
fontSize: '1.5rem',
marginBottom: '8px',
};
const tagsStyle: React.CSSProperties = {
marginTop: '8px',
color: '#555',
};
export function BlogItem({ description, id, title, tags }: BlogItemProps) {
return (
<div className="blog-card"
key={id}
style={cardStyle}>
<h2 style={titleStyle}>{title}</h2>
<p>{description}</p>
<div className="tags" style={tagsStyle}>
<strong>Tags:</strong> {tags.join(', ')}
</div>
</div>
);
}

As we can see, we’ve defined the Blog entity along with the Blog Card that we can use to build our feature to view the blogs. To view its full implementation, check these components out on Bit Cloud.

But, here’s how your local server should look afterward:

2: Building The Features Layer

Next, let’s build the feature — get-blog-posts. To do so, we’ll need one segments:

  1. model: Let's define a hook that fetches the data.

To do so, let’s create a React components:

bit create react-hook features/get-blog-posts/model

Afterward, you should see the output shown below:

Open up the model.ts file and include the code below to fetch the blogs:

import { useBlogStore } from '@dummyorg/fsd.entities.blog.model';
import { useEffect } from 'react';

export function useGetBlogs() {
const { blogs, getBlogs, loading } = useBlogStore();
useEffect(() => {
getBlogs();
});
return { blogs, loading };
}

3: Building The Widgets Layer

Next, let’s build the widget — blog-list that holds the collection of blogs. To do so, let's create a slice called - blog-list and a segment called - ui within the slice. This can be done using the command:

bit create react widgets/blog-list/ui

You should see a new directory added:

Open up the ui.tsx and add the code below:

import React from 'react';
import { Blog } from '@dummyorg/fsd.entities.blog.model';
import { BlogItem } from '@dummyorg/fsd.entities.blog.ui.blog-item';

export type UiProps = {
blogs: Blog[]
};
export function Ui({ blogs = [] }: UiProps) {
return blogs.map((blog) => (<BlogItem
key={blog.id}
description={blog.description}
id={blog.id}
tags={blog.tags}
title={blog.title}
/>))
}

If you navigate to your local server, you should see the output:

You can explore the full implementation of the component over on Bit Cloud.

4: Building The Pages Layer

Next, let’s build a page that holds the blog list together. To do so, let’s create a slice — blog-list and a segment - ui. To do so, run the command:

bit create react pages/blog-list/ui

This will generate the output:

Next, open the ui.tsx and include the snippet:

import React from 'react';
import { useGetBlogs } from '@dummyorg/fsd.features.get-blog-posts.model';
import { BlogList } from '@dummyorg/fsd.widgets.blog-list.ui';

export function Ui() {
const { blogs, loading } = useGetBlogs();
if (loading) {
return <p>
Posts are loading...
</p>
}
return (
<BlogList
blogs={blogs}
/>
);
}

You should see the output shown below in your local server:

And just like that, we set up our Pages! To explore this in detail, check it out on Bit Cloud.

5: Building The App Layer

Finally, let’s create an app to consume all of this. To do so, run the following command:

bit create react-app app

This will create an app component and will generate the output:

To make this app loadable outside of the Bit server, run the command:

bit use app

To ensure your app is loadable, run the command:

bit app list

If your app is loadable, you should see the output:

Next, open the file — app.tsx and include the snippet:

import React from "react";
import { BlogListPage } from "@dummyorg/fsd.pages.blog-list.ui";

export function App() {
return <BlogListPage />
}

Next, launch your app by running the command:

bit run app

You should see the app launched on localhost with the output:

To view the full implementation of the app, check it out on Bit Cloud.

Wrapping Up

As you saw, Feature Sliced Design is quite advantageous when you’re organizing your application in a well-defined and structured manner. It let’s your team navigate across your project quickly and help understand features better.

If you’re interested in exploring the app we created, check it out on Bit Cloud.

I hope you found this article helpful. Do try out FSD for yourself, and let me know your thoughts on this pattern!

Thank you for reading.

--

--