The file/directory structures we show below are best for SPA (Single Page Applications). You can adopt some ideas for your Next.js or Remix projects - but both frameworks adopt a specific method of organizing your files (App Router for Next.js, Nested Routing for Remix)
This directory structure is based on the bulletproof-react repo - it is an absolute goldmine for good practices and architecture of large/enterprise React application.
We're looking for open-source contributors to help us add the configurations for Remix + Next.js here as well, so that those directory structures can be understood. We'd love the help!
To avoid falling victim to analysis paralysis in this area: don't spend more than 5 minutes trying to plan a folder structure that will meet all of your future project needs. It's usually better to simply organize as you go. If you don't know what to do yet - just keep everything in the same flat folder (src). Once you get 10+ files in there that each has its own concern, you'll start to see the patterns that should dictate your folder structure.
Naming Cheatsheet - naming things consistently gets easier with this advice
Deep Dive | Writing Functional Components
Here, we introduce a delightful but scalable component format that you can adopt in your projects. A consistent format will make it easier for you to maintain components in the long run.
Imports and constants
Prop type definition
Component State
Other hooks
Effects
Helper functions (scoped)
JSX (return statement)
Abstracted JSX only used in this file
Component Format
/** * 1. Imports and constants * You can configure an alias in your build tool that allow you to reference a root or specific folder * leads to cleaner imports and not nested spaghetti code like ../../../../some/random/folder/far/away */import { blahblahblah } from'@/features/blah'import { UserContext } from'@/contexts/user'// for additional organization in the imports section ^^, you can// alphebetize them with a package like @trivago/prettier-plugin-sort-importsconstoperations= {'+': (left:number, right:number):number=> left + right,'-': (left:number, right:number):number=> left - right,'*': (left:number, right:number):number=> left * right,'/': (left:number, right:number):number=> left / right,}/** * 2. Prefer simple function definition over const definition * e.g. const Calculator: React.FC<Props> = () => {} */exportfunctionCalculator({left, operator, right}) {/** * 3. Component State */const { user } =useContext(UserContext);const [someState,setSomeState] =useState();const [someOtherState,setSomeOtherState] =useState();constresult= operations[operator](left, right)constsomeCondition=true;/** * 4. Other hooks */constcachedValue=useMemo(calculateValue, dependencies)constcachedFn=useCallback(fn, dependencies)/** * 5. Effects */useEffect(() => {// your effect codereturn () => {// your effect cleanup code } }, dependencies);/** * 6. Helper functions */functionhelperFunctionThatReliesOnComponentState() {cachedFn(cachedValue); }/** * 7. JSX / Return statement * Advice: you can keep your return statements clean by moving static JSX to their own micro-components at the bottom of this file in step 8. */return ( {someCondition ? <ShortenedMarkup /> : ( <div> <code> {left} {operator} {right} = <output>{result}</output> </code> </div> )} );}/** * 8. Abstracted JSX used only in this file */functionShortenedMarkup() {return ( <div> <div> <div> <div> <div> Some UI content that doesn't rely on the props/state of the component above </div> </div> </div> </div> </div> );}
Eslint and Prettier can also be very effective at enforcing project standards and consistency. Start with the examples provided in the bulletproof-react repo:
At a minimum, you should always have an eslint configuration in your project, but running both (eslint is more for catching code smells and potential bugs, and prettier is for opinionated formatting) is recommended.
Getting engineers to properly document their code can sometimes be difficult (and slow down team productivity!)
The question typically isn't "how can we convince our team and stakeholders that well-written documentation is worth it". The right question to ask is "how can we make writing and maintaining documentation easier for our developers". Everyone knows great docs are worth their weight in gold. The hard part is writing great docs.
Use TypeScript - TypeScript can create all sorts of documentation that your IDE (like Visual Studio Code) can display to you while coding. Using TypeScript properly means that all the engineers on your team can:
Analyze function signatures, parameters, and their return types without leaving the file you are working in
Autocomplete while typing, IntelliSense, etc.
Way more!
It also prevents an entire class of bugs from arising, and gives IDE more capabilities (Intellisense, auto-complete, etc.)
Storybook for components and UI libraries - leveraging something like Storybook makes it easier for team members to explore the various states/functionality of your reusable components. It also makes it easier to create entire design systems that can be used across your organization
This would make it easy for engineers AND non-engineers peripheral to the project to browse the various components and their different states (centralized interactive design system)
Commenting systems like JSDoc - technology like this assumes you write plentiful documentation in comments next to your code, and those same comments can be parsed to generate actual HTML webpages that you can navigate, view, etc. It basically tries to take all of your comments throughout your codebase and generate swagger-like documentation for it. If you did BOTH of the above options (1 and 2) this may not be necessary, but if one or both of the options above isn't viable for you then this may be helpful.
HTML pages are generated by tracking your jsdoc comments and creating a navigable website (a type of documentation)
You may hear that you can "write TypeScript" with jsdoc comments, and while that is technically true, we find that writing application code that way can be counter-productive. Maintaining a library is a different story - but if you are writing application code, we wouldn't seriously consider this as an approach to "writing TypeScript", it's probably worth it to just learn the syntax of TypeScript.