Awesome Open Source
Awesome Open Source

WIP - React project styleguide

This document is in "work in progress" state, and may be modified as needed

ReactJS applications can scale very well, but can be really chaotic in no time. This styleguide tries to solve some common problems of large react applications.

Purpose

Create and documentate an opinionated ReactJS style guide for component-based projects.

Contributing

Every help to improve this document is welcome.

I suggest to open an issue, then tag with the following labels:

  • help wanted

    Any misunderstood concept, question or doubt.

  • opinion

    Any opinion about any concept.

  • enhancement

    Ideas to improve the document.

  • grammar

    Grammar errors or misspelled words. (English is not my native language).

Summary

  1. Folder Structure
  2. Components
    1. Elements
    2. Modules
    3. Features
    4. Sub-components
    5. Third party components
    6. Reusing component logics
    7. Styling
  3. Commits

Folder structure

This styleguide covers only /src folder.

/src
  /components
    /elements
      /ElementName
        ElementName.js
        ElementName.spec.js
        ElementName.stories.js
        ElementName.styles.js
        index.js
    /modules
      /ModuleName
        ModuleName.js
        ModuleName.spec.js
        ModuleName.stories.js
        ModuleName.styles.js
        index.js
    /features
      /FeatureName
        FeatureName.js
        FeatureName.spec.js
        FeatureName.stories.js
        FeatureName.styles.js
        index.js
    index.js
  /pages
    /my-page
      ...
    /another-page
      ...
    router.js
  /lib
    /stateManager
      /actions
        ...
      /reduces
        ...
      index.js
    /utils
      ...
  /static
    /styles
      reset.css
      another-static-style.css
    /fonts
      my-font.woff
    /images
      my-image.jpg
  index.js

Back to top

Components

react-create-component can be used to help component creation.

Each component has the following structure:

ModuleName.js
ModuleName.spec.js
ModuleName.stories.js
ModuleName.styles.js
index.js

Where:

  • ComponentName.js
    • The component itself.
  • ComponentName.spec.js
    • Component's test cases.
  • ComponentName.stories.js:
    • Storybook, or any other component documentation file, replacing ".stories" if needed.
  • ComponentName.styles.js: Component styles, it can be any CSS or CSS-in-JS solution.
  • index.js: Component's index, any HOC should be applied here.

Elements

In this system elements are the smallest type of component, they should be highly encapsulable, therefore stateless and also shouldn't contain margins, gaps or any style that might interfere with the overall style of the page.

Examples: Button, Input, ...

Element rules:

  • Elements doesn't have sub-components.

    Why? Elements should be small and has single responsability, if it needs any subcompenent, it should be a module.

  • Elements can not be broken.

    Why? If an element can be broken in pieces, it is not the smallest as possible.

  • Every element should repass unnused props to their root element.

    Why? It increase reusability, repassing remaining props to the root component makes gives them the ability to change html attributes, such as data-attributes, width, height, ...

  • Elements shouldn't have margins.

    Why? Give margins to an element makes it hard to align and reuse.

  • Elements should be stateless.

    Why? Elements should be easy to be controlled by their parents, doing this with a stateful component can be really tricky.

Element sample:

import React from 'react';
import { string, node } from 'prop-types';

function MyElement({
  name,
  ...props,
}) {
  return (
    <p {...props}>
      I am the {name} element!
    </p>
  );
}

MyElement.defaultProps = {
  name: 'default name',
};

MyElement.propTypes = {
  name: string,
};

export default MyElement;

Modules

Modules can use elements to create more complex components, they can have a state and should be easy to reuse, they rearange elements or specific module components, adding margins and modifing to elements, but they should't have margins or gaps on their own.

Modules might have sub-components.

Examples: Menu, Dropdown, ...

Module rules:

  • Modules might have sub-components.

    Why? There are some modules that makes sense to split in other components as they increase their size, but not every module's component will be reused in other places instead the module itself, check sub-components section to learn more.

  • Modules shouldn't have margins.

    Why? Give margins to an module makes it hard to align and reuse.

  • Modules should use elements if they can.

    Why? Elements should be reused as much as possible to mantain the consistence of the application, but modules also can change element's styles to make then fit as best as possible.

  • Modules can be stateful when necessary.

    Why? Modules should be easy to be controlled by their parents, but, as they are more complex components, sometimes it needs to have an state.

  • Modules should avoid getDerivedStateFromProps to manipulate their state.

    Why? Read this article.

Module sample:

import React from 'react';
import { string, node } from 'prop-types';

import { MyElement } from '../..';

function MyModule({
  title,
  children
  ...props,
}) {
  return (
    <section>
      <h1>{title}</h1>
      <MyElement name="My Element Name" />
      {children}
    </section>
  );
}

MyModule.defaultProps = {
  title: 'My title',
  children: null,
};

MyModule.propTypes = {
  title: string,
  children: node,
};

export default MyModule;

Features

Features are complex components, each of them must implement a feature, they use modules and elements to achieve that, also, they can have subfolders with specific components for that feature.

Features can connect with state managers and they meant to be independent of any other feature.

If the component does not implements a feature, it should be a module.

Features might have sub-components.

Examples: Authentication, Graphs, Search ...

Feature rules:

  • Features might have sub-components.

    Why? Features usually has multiple components inside, and within then, there are a great chance that some components will be created specifically for that feature, check sub-components section to learn more.

  • Features shouldn't have margins.

    Why? Give margins to an feature makes it hard to align and reuse.

  • Features should use elements and modules if they can.

    Why? Elements and modules should be reused as much as possible to mantain the consistence of the application, features can also change elements and modules styles.

  • Features can be stateful.

    Why? Features are end to end funcionality components, implementing logics and ui behaviour. Check Reusing component logics section to learn more.

Feature sample:

import React, { Component } from 'react';

import {
  MyElement,
  MyModule,
} from '../..';

class MyFeature extends Component {
  state = {
    didSomething: false,
  }

  doSomeAwesomeThing = () => {
    // Some code here...

    this.setState({
      didSomething: true,
    });
  }

  render() {
    const {
      didSomething,
    } = this.state;

    return (
      <section>
        <MyModule title="Awesome title">
          <MyElement name="Another Name" />
        </MyModule>

        {
          didSomething && 'I did an awesome thing!'
        }

        <button onClick={this.doSomeAwesomeThing}>
          I will do something!
        </button>
      </section>
    );
  }
}

export default MyFeature;

Sub-components

TO DO: Complementary information and folder structure

For more information, check this React Native component API, or this article.

Back to top

Third party components

Using third party components can be very helpful to prevent "reinventing the wheel", and can reduce a lot of time spent coding.

Although, sometimes we need to change our projects dependencies to better fit our need, and it can be really stressfull to refactour all of existing code that uses that dependency.

Here is a tip: All third party components should be reexported by another component that will be used within your application.

Why? Using third party components through your entire aplication can be really tricky to refactor, instead of using it directly, I reccomend to reexport it, so, you will have centered file to refactor if that component's API changes, or be replaced by another one.

Third party component sample:

// ./src/components/elements/ClickOut/index.js
import ClickOut from 'react-click-out';

/*
  Prefer explicit exports.
  Avoid export * from 'something' when
  reexporting third party components,
  which may cause a refactor hazard.
*/
export default ClickOut;
// ./src/components/modules/MyModule/MyModule.js
import React from 'react';

import { ClickOut, MyElement } from '../..';

function MyModule() {
  return (
    <ClickOut onClick={/* Do something... */}>
      <MyElement name="My Element Name" />
      ...
    </ClickOut>
  );
}

export default MyModule;

Back to top

Reusing component logics

TO DO

Back to top

Styling

TO DO

Back to top

Commits

Commitizen's cli is recomended to ensure right commit messages.

Back to top

References and special thanks

This styleguide is strongly influenced by the awesome Bradfrost's atomic design.

Other references:

Back to top

License

This document is MIT licensed.


Get A Weekly Email With Trending Projects For These Topics
No Spam. Unsubscribe easily at any time.
react (5,159
reactjs (1,026
architecture (323
styleguide (96
style-guide (82
structure (28

Find Open Source By Browsing 7,000 Topics Across 59 Categories