/ React

Structuring React Components

I've been using React since 2016. React has changed a lot since then, but the fundamentals have stayed the same.

In the last couple of years, the Product Hunt team and I have settled on a way to structure our components.

The goal is to make understanding components easier.

Every component should provide quick answers to the following questions.

  • What are the props this component needs?
  • What does this component do?
  • What are the component's states?
  • What is the component's logic?
  • How can we trace the logic?
  • Is it easy to refactor?

Component as directory

Components are encapsulated in directories that contain their individual parts.

The only public thing there is the main index file.
Everything else is considered private.

Here is what the component directory looks like:

~/components/[Component]/index.tsx                // default export is the used component
~/components/[Component]/Fragment.graphql         // GraphQL fragment for the component
~/components/[Component]/Mutation.graphql         // GraphQL mutation for the component
~/components/[Component]/styles.module.css        // css-module, with styles for the component
~/components/[Component]/[icon].svg               // icon asset
~/components/[Component]/utils.ts                 // helper functions/classes for the component
~/components/[Component]/utils.test.ts            // tests for helper functions/classes
~/components/[Component]/[SubComponent]/index.tsx // when this component is complex, it's might
~/components/[Component]/[SubComponent]/...       // be split it into multiple components
~/components/[Component]/[SubComponent]/...       // they are private and shouldn't be imported
~/components/[Component]/[SubComponent]/...       //  from outside its parent component

Component

Every component we have, has an entry point like the following:

// components/Component/index.js

// 1) Component props
// always name this "IProps"
// this makes it easy to quickly see the expected props for this component
interface IProps {
  // ...
};

// 2) Main component export
// immediately after, put the component name since this is what this package is all about
// use default function values for default props
export default function ComponentName({ prop1, prop2 = 'default value' }: IProps) {
  // component is structured by
  // 2.1) Hooks
  // if this is more than 5 lines, a custom hook is extracted
  // more about this - https://blog.rstankov.com/extract-react-hook-refactoring/

  // 2.2) Guard clauses
  if (someCondition) {
    return null;
  } 

  if (someOtherCondiation) {
    return <SamePlaholder />
  }

  // 2.3) The happy path of the component
	return <div>My component</div>;
}

// 3) Everything else...
// Here's where the smaller helper hooks, utils, and components live.
// If those get bigger, they can be moved into separate files in the component directory

This structure makes it very easy to scan component and find what you are looking for.

Conclusion

This structure has served us well, and its fundamentals have stayed the same when migrating from classes to hooks, from flow type to TypeScript, and so.

If you have any questions or comments, you can ping me on Twitter.