|
| 1 | +# React + TypeScript Feature-First Style Guide |
| 2 | + |
| 3 | +A **feature-first** approach to building **scalable, maintainable, and predictable** React applications with TypeScript. Designed for **clarity, consistency, and efficiency**, this guide minimizes cognitive load and enforces best practices. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## Core Principles |
| 8 | + |
| 9 | +✅ **Feature-First Development** – Features are self-contained and organized around routes.\ |
| 10 | +✅ **Minimal Cognitive Load** – Engineers should immediately know where code belongs.\ |
| 11 | +✅ **Predictability & Consistency** – Every feature follows the same structure.\ |
| 12 | +✅ **No Unnecessary Abstractions** – Complexity is only introduced when necessary.\ |
| 13 | +✅ **Automation Over Convention** – Enforceable via ESLint/Prettier instead of manual rules.\ |
| 14 | + |
| 15 | +📖 **Additional Resources:** |
| 16 | + |
| 17 | +- [Google TypeScript Style Guide](https://google.github.io/styleguide/tsguide.html) |
| 18 | +- [Airbnb React Style Guide](https://airbnb.io/javascript/react/) |
| 19 | + |
| 20 | +--- |
| 21 | + |
| 22 | +## Feature-First Folder Structure |
| 23 | + |
| 24 | +### **What is a Feature?** |
| 25 | + |
| 26 | +A **feature** is a **self-contained module** that represents: |
| 27 | + |
| 28 | +- A **route** (`/guides/:guideId`) |
| 29 | +- A **significant UI section** |
| 30 | +- **Reusable business logic specific to a domain** |
| 31 | + |
| 32 | +### **Feature Structure Example** |
| 33 | + |
| 34 | +``` |
| 35 | +pages/guides/search → Feature (route: `/guides/search`) |
| 36 | + GuideSearch.tsx → Main component |
| 37 | +
|
| 38 | +pages/guides/:guideId → Feature (route: `/guides/:guideId`) |
| 39 | + Guide.tsx → Main component |
| 40 | + hooks/useGetGuideQuery.ts → Query for getting details |
| 41 | + hooks/useGuideMetricShare.ts → Handles copy-to-clipboard/sharing |
| 42 | + components/GuideHero.tsx → Feature-scoped component |
| 43 | +
|
| 44 | +pages/profile/:accountHandle → Feature (route: `/profile/:accountHandle`) |
| 45 | + Profile.tsx → Main component |
| 46 | +
|
| 47 | +pages/profile/:accountHandle/guides → Feature (route: `/profile/:accountHandle/guides`) |
| 48 | + ProfileGuides.tsx → Main component |
| 49 | +``` |
| 50 | + |
| 51 | +✅ **Flat structure:** No deep nesting inside features.\ |
| 52 | +✅ **Feature-scoped:** Hooks and components belong inside the feature they support.\ |
| 53 | +✅ **No `common/` folder** – Instead, use `src/hooks/` for sitewide hooks and `src/components/` for reusable components. |
| 54 | + |
| 55 | +--- |
| 56 | + |
| 57 | +## Component Structure |
| 58 | + |
| 59 | +**Ordering Inside a Component:**\ |
| 60 | +1️⃣ **Hooks** (`useState`, `useEffect`, etc.) |
| 61 | +2️⃣ **Local Variables** (constants, derived values) |
| 62 | +3️⃣ **useEffect Hooks** (side effects, lifecycle logic) |
| 63 | +4️⃣ **Event Handlers & Functions** |
| 64 | +5️⃣ **Return Statement (JSX)** |
| 65 | + |
| 66 | +✅ **Example Component:** |
| 67 | + |
| 68 | +```tsx |
| 69 | +export const Profile = () => { |
| 70 | + const { hasError, isLoading, profileData } = useGetProfileQuery() |
| 71 | + |
| 72 | + if (isLoading) return <ProfileLoading /> |
| 73 | + |
| 74 | + if (hasError) return <ProfileEmpty /> |
| 75 | + |
| 76 | + return ( |
| 77 | + <section> |
| 78 | + <ProfileHero /> |
| 79 | + <ProfileContent /> |
| 80 | + </section> |
| 81 | + ) |
| 82 | +} |
| 83 | +``` |
| 84 | + |
| 85 | +--- |
| 86 | + |
| 87 | +## Naming Conventions |
| 88 | + |
| 89 | +| Item | Naming Convention | |
| 90 | +| --------------------------------- | ---------------------------------------------------------- | |
| 91 | +| **Variables, Functions, Hooks** | `camelCase` (e.g., `getUserProfile`) | |
| 92 | +| **Components, Enums, Interfaces** | `PascalCase` (e.g., `UserProfileCard`) | |
| 93 | +| **Folders** | `kebab-case` (e.g., `profile-settings/`) | |
| 94 | +| **Constants** | Defined within feature files, not in a global folder. | |
| 95 | +| **GraphQL Queries/Mutations** | `camelCase` inside operations, `PascalCase` for file names | |
| 96 | + |
| 97 | +--- |
| 98 | + |
| 99 | +## GraphQL Queries & Mutations |
| 100 | + |
| 101 | +| Type | Placement | |
| 102 | +| ------------------------- | --------------------------------- | |
| 103 | +| **Feature-based Queries** | Inside `pages/featureName/hooks/` | |
| 104 | +| **Sitewide Queries** | Inside `src/hooks/` | |
| 105 | + |
| 106 | +✅ **Correct Naming Examples:** |
| 107 | + |
| 108 | +```graphql |
| 109 | +query GetGuide($id: ID!) { ... } # ✅ Fetches full guide details |
| 110 | +query GetGuideEvents($guideId: ID!) { ... } # ✅ Fetches related guide events |
| 111 | +query GetGuideImages($guideId: ID!) { ... } # ✅ Fetches guide images |
| 112 | +``` |
| 113 | + |
| 114 | +📖 **Additional Resources:** |
| 115 | + |
| 116 | +- [GraphQL Best Practices](https://graphql.org/learn/best-practices/) |
| 117 | + |
| 118 | +--- |
| 119 | + |
| 120 | +## Types & Interfaces |
| 121 | + |
| 122 | +| When to Use | Rule | |
| 123 | +| -------------------------------------------------------------- | ------------------------------------------------------- | |
| 124 | +| **Props for Components** | Use `interface` (e.g., `interface ProfileProps {}`) | |
| 125 | +| **Everything Else (Utilities, Hooks, GraphQL, API Responses)** | Use `type` (e.g., `type UseGetProfileQueryResult = {}`) | |
| 126 | +| **Extracting Subsets of Types** | Use `Pick<>` and `Omit<>` | |
| 127 | + |
| 128 | +**Additional Resources:** |
| 129 | + |
| 130 | +- [TypeScript Handbook: Types vs. Interfaces](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html) |
| 131 | + |
| 132 | +--- |
| 133 | + |
| 134 | +## Feature Flags |
| 135 | + |
| 136 | +| Item | Rule | |
| 137 | +| ----------- | ----------------------------------------------------- | |
| 138 | +| **Storage** | Centralized in `config/feature-flags/featureFlags.ts` | |
| 139 | +| **Usage** | Accessed via `useFlag` hook | |
| 140 | +| **Cleanup** | Feature flags should be short-lived | |
| 141 | + |
| 142 | +📖 **Additional Resources:** |
| 143 | + |
| 144 | +- [Feature Flags Best Practices](https://martinfowler.com/articles/feature-toggles.html) |
| 145 | + |
| 146 | +--- |
| 147 | + |
| 148 | +## ✍️ Comments & Documentation |
| 149 | + |
| 150 | +✅ **Code should be self-explanatory; avoid unnecessary comments.** |
| 151 | +✅ **Use JSDoc `@todo` for tracking future work.** |
| 152 | +✅ **Only document "why", not "what" the code does.** |
| 153 | + |
| 154 | +✅ **Example:** |
| 155 | + |
| 156 | +```ts |
| 157 | +/** @todo Remove this workaround when the new API version is available */ |
| 158 | +const getUserPreferences = async (userId: string) => { |
| 159 | + return await fetch(`/api/preferences/${userId}`) |
| 160 | +} |
| 161 | +``` |
| 162 | + |
| 163 | +**Additional Resources:** |
| 164 | + |
| 165 | +- [Clean Code Principles](https://www.oreilly.com/library/view/clean-code/9780136083238/) |
| 166 | + |
| 167 | +--- |
| 168 | + |
| 169 | +## Final Thoughts |
| 170 | + |
| 171 | +**This cheat sheet ensures clarity, predictability, and minimal cognitive load.** |
| 172 | + |
| 173 | +✅ **Keep rules minimal & enforceable.**\ |
| 174 | +✅ **Follow automation-first principles.**\ |
| 175 | +✅ **Structure code in a way that scales naturally.** |
| 176 | + |
| 177 | +This is your definitive **React + TypeScript Feature-First Style Guide**, designed to **reduce thinking, improve efficiency, and create scalable applications effortlessly.** |
0 commit comments