Project Standards
What You'll Learn
- Various File/Directory Structures for your React app
- A file template for consistent patterns in your UI components
- Tips for authoring and maintaining project documentation
File/Directory Structures
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)
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.
Component Files
- Clean Code JavaScript - advice for writing clear, concise code in JavaScript
- Naming Cheatsheet - naming things consistently gets easier with this advice
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-imports
const operations = {
'+': (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> = () => {}
\*/
export function Calculator({left, operator, right}) {
/\*\*
- 3. Component State
\*/
const { user } = useContext(UserContext);
const [someState, setSomeState] = useState();
const [someOtherState, setSomeOtherState] = useState();
const result = operations[operator](left, right)
const someCondition = true;
/\*\*
- 4. Other hooks
\*/
const cachedValue = useMemo(calculateValue, dependencies)
const cachedFn = useCallback(fn, dependencies)
/\*\*
- 5. Effects
\*/
useEffect(() => {
// your effect code
return () => {
// your effect cleanup code
}
}, dependencies);
/\*\*
- 6. Helper functions
\*/
function helperFunctionThatReliesOnComponentState() {
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
\*/
function ShortenedMarkup() {
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>
); }
Linting and Formatting
Eslint and Prettier can also be very effective at enforcing project standards and consistency. Start with the examples provided in the bulletproof-react repo:
- .eslintrc.js (eslint config file)
- .prettierrc (prettier config file)
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.
Maintaining Documentation
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 -
Storybook for React Tutorial
- 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) - See how Storybook works
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 difficult in a way that simply learning TypeScript is not. 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" - in other words, we'd recommend to just learn the syntax of TypeScript. - If you are interested in this approach, read JSDoc as an alternative TypeScript syntax by Alex Harri