{"id":25677975,"url":"https://github.com/mlane/react-typescript-style-guide","last_synced_at":"2026-05-08T06:12:12.843Z","repository":{"id":278828427,"uuid":"936893693","full_name":"mlane/react-typescript-style-guide","owner":"mlane","description":"An opinionated React + TypeScript style guide for writing scalable, maintainable, and consistent code.","archived":false,"fork":false,"pushed_at":"2025-02-23T02:09:39.000Z","size":24,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-23T03:18:32.968Z","etag":null,"topics":["best-practices","code-style","component-architecture","feature-flags","folder-structure","frontend-development","graphql","open-source","react","react-typescript","style-guide","styleguide-javascript","typescript"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mlane.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-02-21T21:58:16.000Z","updated_at":"2025-02-23T02:12:48.000Z","dependencies_parsed_at":"2025-02-23T03:18:34.067Z","dependency_job_id":null,"html_url":"https://github.com/mlane/react-typescript-style-guide","commit_stats":null,"previous_names":["mlane/react-ts-style-guide","mlane/react-typescript-style-guide"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlane%2Freact-typescript-style-guide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlane%2Freact-typescript-style-guide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlane%2Freact-typescript-style-guide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlane%2Freact-typescript-style-guide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mlane","download_url":"https://codeload.github.com/mlane/react-typescript-style-guide/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240502671,"owners_count":19811852,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["best-practices","code-style","component-architecture","feature-flags","folder-structure","frontend-development","graphql","open-source","react","react-typescript","style-guide","styleguide-javascript","typescript"],"created_at":"2025-02-24T15:27:45.227Z","updated_at":"2026-05-08T06:12:12.831Z","avatar_url":"https://github.com/mlane.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003e **\"Any fool can write code that a computer can understand. Good programmers write code that humans can\n\u003e understand.\"**  \n\u003e — _Martin Fowler_\n\n# 🚀 React + TypeScript Style Guide\n\nA **structured, scalable, and opinionated** style guide for building **maintainable React applications** with\n**TypeScript**. This guide ensures **consistency, clarity, and best practices** across projects.\n\n## 📖 Table of Contents\n\n- [Philosophy](#-philosophy)\n- [Folder Structure](#-folder-structure)\n- [Component Structure](#-component-structure)\n- [Functions \u0026 Utilities](#-functions--utilities)\n- [GraphQL Queries](#-graphql-queries)\n- [Feature Flags](#-feature-flags)\n- [Types \u0026 Interfaces](#-types--interfaces)\n- [Comments \u0026 Documentation](#-comments--documentation)\n- [Contributing](#-contributing)\n- [License](#-license)\n- [References \u0026 Inspirations](#-references--inspirations)\n\n---\n\n## 🧠 Philosophy\n\nThis style guide is designed to ensure **consistency, readability, and maintainability** in React + TypeScript projects.\nBy following a structured approach, we aim to reduce cognitive load, improve collaboration, and make codebases easier to\nscale.\n\n### 🔹 Core Principles\n\n- **Minimal Mental Overhead**  \n  Code should be **easy to scan and understand** without requiring excessive comments or context switching. Developers\n  should be able to predict where things are located and how they are structured.\n\n- **Predictability**  \n  Every file and component follows a **consistent structure**, minimizing ambiguity. Naming conventions, folder structures,\n  and function placements should remain consistent across the entire codebase.\n\n- **Clarity Over Flexibility**  \n  While flexibility can be useful, **clarity is prioritized**. The goal is not to support every possible way of writing\n  code but to ensure that code is **uniform and easy to maintain**.\n\n- **Encapsulation**  \n  Each feature should be **self-contained**, meaning components, hooks, and utilities related to a feature should live\n  in the same folder. This improves modularity and reduces cross-dependencies.\n\n- **No Unnecessary Abstraction**  \n  Over-engineering leads to **harder-to-read** code. We avoid unnecessary wrapper functions, excessive prop drilling,\n  and premature optimizations unless there is a **clear** need for them.\n\n- **Early Returns for Simplicity**  \n  When dealing with conditionals, we **return early** to **reduce nesting** and improve readability.\n\n- **Separation of Concerns**  \n  Logic, UI, and state management should be **properly separated** to improve maintainability. Business logic should\n  live in hooks or utility functions rather than in the UI layer.\n\n### ✅ Summary\n\nBy following this guide, teams can write **cleaner, more scalable, and easier-to-maintain** code. The focus is on\n**consistency, clarity, and minimal cognitive load** while following modern React + TypeScript best practices.\n\n---\n\n## 📂 Folder Structure\n\nA **structured, feature-based folder organization** ensures **scalability, maintainability, and readability**. This\nstructure keeps related files **encapsulated** while providing clear separation between **shared logic** and\n**feature-specific implementations**.\n\n### 🔹 General Folder Structure\n\n- **Feature-based structure (`pages/featureName/`)**\n\n  - Each feature has its own folder inside `pages/`.\n    - **Example:** `pages/profile/` contains all Profile-related logic.\n  - Hooks related to a specific feature must be placed inside `hooks/` within that feature’s folder.\n    - **Example:** `pages/profile/hooks/useGetProfileQuery.ts` for a Profile-specific query.\n    - Hooks shared across multiple features should remain in `common/hooks/`.\n  - **Recommended depth:** While **there's no strict limit**, keeping **features within three levels**\n    (`pages/profile/common/ProfileHero/`) improves maintainability.\n\n- **`common/` for shared logic**\n\n  - Stores **shared UI components, hooks, and utilities**.\n  - **Example:** `common/hooks/useFlag.ts` is reusable across multiple features.\n\n- **Application-wide configurations in `config/`**\n\n  - This folder is **only for external integrations** (e.g., Google Maps, Firebase, Analytics).\n  - **Example:** `config/apollo/ApolloProvider.tsx`.\n\n- **Global constants \u0026 utils (if needed) in `constants/`**\n\n  - This folder is for **app-wide constants and utilities** that are used in **multiple features**.\n  - If a constant or utility **is used in more than one feature**, move it here.\n  - **Example:** `constants/guideUtils.ts` contains `getGuideDetailsUrl` since it is used in multiple places (e.g.,\n    dashboard \u0026 profiles).\n  - **Feature-specific constants and utilities** should remain inside the **respective feature folder** (e.g.,\n    `pages/profile/profileConstants.ts`).\n\n- **Assets Handling**\n\n  - **Fonts remain in `assets/fonts/`** for styling purposes.\n  - **Images belong in a separate repository**, not within `src/`.\n\n- **GraphQL Queries/Mutations stay in the feature root (if needed)**\n\n  - **Example:** `useGetProfileQuery.ts`, `useCreateProfileMutation.ts`.\n  - Makes **auditing API calls easier** without deep nesting.\n\n- **Standardized `index.ts` Usage**\n  - **Wherever applicable, folders should contain an `index.ts` file** to simplify imports.\n  - This allows **cleaner imports** and prevents deep import paths.\n    - **Example:**\n      ```tsx\n      import { ProfileHero } from 'src/pages/profile/common'\n      ```\n      Instead of:\n      ```tsx\n      import { ProfileHero } from 'src/pages/profile/common/ProfileHero/ProfileHero'\n      ```\n  - Recommended for:\n    - Feature directories (`pages/profile/index.ts`)\n    - Common utilities and hooks (`common/hooks/index.ts`)\n    - Nested components (`pages/profile/common/ProfileHero/index.ts`)\n  - `index.ts` files can improve import readability, but excessive use of barrel files can introduce problems such as unintended re-exports, circular dependencies, and inefficient bundling. This is especially relevant in SSR frameworks like Remix.\n\n---\n\n### 🔹 Barrel Files \u0026 Wildcard Imports\n\nUsing **barrel files (`index.ts`)** can simplify imports and improve readability, but they should be used with caution. Overuse can lead to unintended re-exports, circular dependencies, and performance issues in certain frameworks like Remix.\n\n#### **When to Use Barrel Files**\n\n- ✅ When grouping related exports within a feature (`pages/profile/index.ts`).\n- ✅ When creating a clean API for shared utilities (`common/hooks/index.ts`).\n- ✅ When improving import readability by reducing deep paths.\n\n#### **When to Avoid Barrel Files**\n\n- ❌ If the file contains a **large number of exports**, making tree-shaking less effective.\n- ❌ If the file is **frequently updated**, causing unnecessary rebuilds.\n- ❌ When using **Remix and other SSR frameworks**, as barrel files can cause issues with bundling.\n- ❌ While `index.ts` files help simplify imports, overusing them as barrel files (re-exporting everything) can lead to unintended re-exports, circular dependencies, and inefficient bundling. This is particularly problematic in SSR frameworks like Remix, where improper tree-shaking can increase load times.\n\n#### **Best Practices**\n\n- Prefer **explicit imports** over `import * as X` to avoid unintended re-exports.\n- Keep barrel files **feature-scoped** rather than app-wide.\n- Be mindful of how **tree-shaking** works in the bundler to avoid unnecessary imports.\n\n**❌ Avoid wildcard (\\*) imports as they increase bundle size, prevent effective tree-shaking, and can introduce unnecessary dependencies.**\n\n- Import unnecessary code, even if unused.\n- Make code harder to track, increasing debugging complexity.\n- Wildcard imports (`import *`) force the bundler to include unused code, increasing bundle size and reducing tree-shaking efficiency.\n\n```ts\nimport * as utils from ‘common/utils’\n```\n\n**✅ Prefer named imports for clarity and tree-shaking**\n\n```ts\nimport { formatDate, getUserProfile } from ‘common/utils’\n```\n\n---\n\n```plaintext\napp/\n├── routes/\n├── src/\n├── assets/\n│   ├── fonts/\n│   │   ├── roboto-bold.woff\n│   │   ├── roboto-bold.woff2\n├── common/\n│   ├── components/\n│   ├── hooks/\n│   │   ├── index.ts\n│   │   ├── useFlag.ts\n├── config/                      # External service integrations only\n│   ├── analytics/\n│   │   ├── index.ts\n│   │   ├── useLucencyNumber.ts\n│   ├── apollo/\n│   │   ├── ApolloProvider.tsx\n│   │   ├── index.ts\n├── constants/                    # Global constants/utils (if used in multiple features)\n│   ├── guideUtils.ts             # Example: Used in multiple features\n│   ├── index.ts\n│   ├── user.ts\n├── pages/\n│   ├── guide/\n│   │   ├── __tests__/\n│   │   │   ├── __mocks__/\n│   │   │   │   ├── index.ts\n│   │   │   │   ├── guideMock.ts\n│   │   │   ├── Guide.test.tsx\n│   │   │   ├── guideUtils.test.ts\n│   │   ├── common/\n│   │   │   ├── __tests__/\n│   │   │   │   ├── GuideBadge.test.tsx\n│   │   │   ├── GuideHero/\n│   │   │   │   ├── __tests__/\n│   │   │   │   │   ├── GuideHero.test.tsx\n│   │   │   │   ├── GuideHero.tsx\n│   │   │   │   ├── GuideHeroLoading.tsx\n│   │   │   │   ├── index.ts\n│   │   │   ├── GuideBadge.tsx\n│   │   │   ├── GuideLoading.tsx\n│   │   │   ├── index.ts\n│   │   ├── hooks/\n│   │   │   ├── index.ts\n│   │   │   ├── useCreateGuideMutation.ts\n│   │   │   ├── useGetGuideQuery.ts\n│   │   │   ├── useUpdateGuideMutation.ts\n│   │   ├── Guide.tsx\n│   │   ├── index.ts               # For cleaner imports\n│   │   ├── guideConstants.ts (if needed)\n│   │   ├── guideUtils.ts (if needed)\n│   │   ├── types.ts (if needed)\n│   ├── profile/\n│   │   ├── __tests__/\n│   │   │   ├── __mocks__/\n│   │   │   │   ├── index.ts\n│   │   │   │   ├── profileMock.ts\n│   │   │   ├── Profile.test.tsx\n│   │   │   ├── profileUtils.test.ts\n│   │   ├── common/\n│   │   │   ├── __tests__/\n│   │   │   │   ├── ProfileHero.test.tsx\n│   │   │   ├── ProfileHero/\n│   │   │   │   ├── ProfileHero.tsx\n│   │   │   │   ├── ProfileHeroLoading.tsx\n│   │   │   │   ├── index.ts\n│   │   │   ├── ProfileLoading.tsx\n│   │   │   ├── ProfileSidebar/\n│   │   │   │   ├── ProfileSidebar.tsx\n│   │   │   │   ├── ProfileSidebarLoading.tsx\n│   │   │   │   ├── index.ts\n│   │   │   ├── index.ts\n│   │   ├── hooks/\n│   │   │   ├── index.ts\n│   │   │   ├── useCreateProfileMutation.ts\n│   │   │   ├── useGetProfileQuery.ts\n│   │   ├── Profile.tsx\n│   │   ├── index.ts               # For cleaner imports\n│   │   ├── profileConstants.ts (if needed)\n│   │   ├── profileUtils.ts (if needed)\n│   │   ├── types.ts (if needed)\n```\n\n---\n\n#### 🔹 Why This Works\n\n- ✅ **Scalability** → Features remain **self-contained**, making it easy to expand the app.\n- ✅ **Encapsulation** → Keeps related files **together**, reducing unnecessary dependencies.\n- ✅ **Readability** → Developers can quickly find what they need **without deep nesting**.\n- ✅ **Predictability** → Standardized naming and placement **eliminate confusion**.\n\n---\n\n## 🎭 Component Structure\n\nA well-structured React component improves **readability, maintainability, and consistency**. This section defines **how\ncomponents should be structured**, including **ordering hooks, variables, functions, and the return statement**.\n\n### 🔹 General Rules for Components\n\n- **Always use functional components (`const MyComponent = () =\u003e {}`)**.\n- **Component file names should match the folder name** if applicable.\n  - **Example:** `ProfileHero.tsx` inside `ProfileHero/` should match the folder name.\n- **Each component should be self-contained** and only handle **one responsibility**.\n- **Avoid deep nesting of JSX**—break into smaller components when necessary.\n- **Keep components under ~150 lines** for readability.\n- **Early return for loading/error states** to reduce indentation.\n- **Hooks, variables, and functions should follow a consistent order**.\n\n---\n\n### 🔹 Component Order\n\nComponents should follow this order:\n\n- 1️⃣ **Hooks** (`useState`, `useEffect`, etc.).\n- 2️⃣ **Variables that are not functions** (local variables, constants, etc.).\n- 3️⃣ **`useEffect` hooks** (side effects).\n- 4️⃣ **Functions (event handlers, derived functions, etc.).**\n- 5️⃣ **Return statement (JSX).**\n\n---\n\n**✅ Example: Standard Component Structure**\n\n```tsx\nexport const Profile = () =\u003e {\n  const navigate = useNavigate()\n  const { accountHandle } = useParams()\n  const { hasError, isLoading, profileData } = useGetProfileQuery(accountHandle)\n  const [searchParams] = useSearchParams()\n  const { id, image } = profileData ?? {}\n\n  useEffect(() =\u003e {\n    // Example: Track analytics\n  }, [])\n\n  const getProfileAvatar = () =\u003e {}\n\n  const getProfileName = () =\u003e {}\n\n  if (isLoading || isEmpty(profileData)) return \u003cProfileLoading /\u003e\n\n  if (hasError) return \u003cProfileEmpty /\u003e\n\n  return (\n    \u003csection\u003e\n      \u003cProfileHero /\u003e\n      \u003cdiv\u003e\n        \u003cProfileSidebar /\u003e\n        \u003cProfileContent /\u003e\n      \u003c/div\u003e\n    \u003c/section\u003e\n  )\n}\n```\n\n---\n\n### 🔹 Return Statement Spacing\n\n- **Always add a blank line before `return`** to visually separate logic from JSX.\n- This improves **readability and scanning** by making the function’s return statement stand out.\n- It helps maintain consistency across the codebase.\n\n**✅ Example:**\n\n```tsx\nexport const Profile = () =\u003e {\n  const { hasError, isLoading, profileData } = useGetProfileQuery()\n\n  if (isLoading || isEmpty(profileData)) return \u003cProfileLoading /\u003e\n\n  if (hasError) return \u003cProfileEmpty /\u003e\n\n  return (\n    \u003csection\u003e\n      \u003cProfileHero /\u003e\n      \u003cdiv\u003e\n        \u003cProfileSidebar /\u003e\n        \u003cProfileContent /\u003e\n      \u003c/div\u003e\n    \u003c/section\u003e\n  )\n}\n```\n\n**❌ Avoid cramming `return` right after logic without spacing.**\n\n```tsx\nexport const Profile = () =\u003e {\n  const { hasError, isLoading, profileData } = useGetProfileQuery()\n  if (isLoading || isEmpty(profileData)) return \u003cProfileLoading /\u003e\n  if (hasError) return \u003cProfileEmpty /\u003e\n  return (\n    \u003csection\u003e\n      \u003cProfileHero /\u003e\n      \u003cdiv\u003e\n        \u003cProfileSidebar /\u003e\n        \u003cProfileContent /\u003e\n      \u003c/div\u003e\n    \u003c/section\u003e\n  )\n}\n```\n\n```tsx\nexport const Profile = () =\u003e {\n  const { hasError, isLoading, profileData } = useGetProfileQuery()\n  return (\n    \u003csection\u003e\n      \u003cProfileHero /\u003e\n      \u003cdiv\u003e\n        \u003cProfileSidebar /\u003e\n        \u003cProfileContent /\u003e\n      \u003c/div\u003e\n    \u003c/section\u003e\n  )\n}\n```\n\n---\n\n## 🔹 Return Formatting in Functional Components\n\nWhen returning JSX in functional components, maintain **consistent spacing** for clarity and readability.\n\n### ✅ General Rules:\n\n- **Use early returns for loading and error states** to reduce nesting.\n- **Single-line early returns should not have extra space before them.**\n- **Multiline return blocks should always be formatted for readability.**\n- **Return statements should not have an unnecessary empty line before them unless inside a conditional block.**\n\n---\n\n**✅ Example: Correct Formatting**\n\n```tsx\nexport const Profile = () =\u003e {\n  const { hasError, isLoading, profileData } = useGetProfileQuery()\n\n  if (isLoading) return \u003cProfileLoading /\u003e\n\n  if (hasError) return \u003cProfileError /\u003e\n\n  return (\n    \u003csection\u003e\n      \u003cProfileHero /\u003e\n      \u003cProfileContent /\u003e\n    \u003c/section\u003e\n  )\n}\n```\n\n**❌ Example: Incorrect Formatting**\n\n```tsx\nexport const Profile = () =\u003e {\n  const { hasError, isLoading, profileData } = useGetProfileQuery()\n\n  if (isLoading) {\n    return \u003cProfileLoading /\u003e\n  }\n\n  if (hasError) {\n    return \u003cProfileEmpty /\u003e\n  }\n\n  return (\n    \u003csection\u003e\n      \u003cProfileHero /\u003e\n      \u003cProfileContent /\u003e\n    \u003c/section\u003e\n  )\n}\n```\n\n### ✅ Summary\n\n- **One-liner early returns should not have extra space.**\n- **Multiline return blocks should always be formatted for readability.**\n- **Use separate lines when return statements are inside a block.**\n\n---\n\n### 🔹 Early Returns for Simplicity\n\nTo improve readability and reduce indentation, always **return early** in conditionals rather than nesting them inside larger blocks.\n\n✅ **Example: Using Early Return for Cleaner Code**\n\n```tsx\nexport const Profile = () =\u003e {\n  const { hasError, isLoading, profileData } = useGetProfileQuery()\n\n  if (isLoading) return \u003cProfileLoading /\u003e\n\n  if (hasError) return \u003cProfileEmpty /\u003e\n\n  return (\n    \u003csection\u003e\n      \u003cProfileHero /\u003e\n      \u003cProfileContent /\u003e\n    \u003c/section\u003e\n  )\n}\n```\n\n**❌ Example: Nested Conditionals (Harder to Read)**\n\n```tsx\nexport const Profile = () =\u003e {\n  const { hasError, isLoading, profileData } = useGetProfileQuery()\n\n  if (isLoading) {\n    return \u003cProfileLoading /\u003e\n  } else {\n    if (hasError) {\n      return \u003cProfileEmpty /\u003e\n    } else {\n      return (\n        \u003csection\u003e\n          \u003cProfileHero /\u003e\n          \u003cProfileContent /\u003e\n        \u003c/section\u003e\n      )\n    }\n  }\n}\n```\n\n---\n\n### 🔹 JSX Formatting Rules\n\n- **One-line return when there is no logic.**\n\n```tsx\nexport const Profile = () =\u003e \u003csection\u003e...\u003c/section\u003e\n```\n\n- **Use multiple lines for JSX if it improves readability.**\n\n```tsx\nexport const Profile = () =\u003e (\n  \u003csection\u003e\n    \u003cProfileHero /\u003e\n    \u003cProfileSidebar /\u003e\n  \u003c/section\u003e\n)\n```\n\n---\n\n### 🔹 Function \u0026 Hook Spacing Rules\n\n- **No extra space between hooks and variables.**\n\n```tsx\nconst navigate = useNavigate()\nconst { accountHandle } = useParams()\nconst { hasError, isLoading, profileData } = useGetProfileQuery(accountHandle)\nconst [searchParams] = useSearchParams()\nconst { id, image } = profileData ?? {}\n```\n\n- **Add a space between function declarations for readability.**\n\n```tsx\nconst getProfileAvatar = () =\u003e {}\n\nconst getProfileName = () =\u003e {}\n```\n\n- **Space out `useEffect` from other hooks.**\n\n```tsx\nconst navigate = useNavigate()\nconst { accountHandle } = useParams()\n\nuseEffect(() =\u003e {\n  // Example: Sync data on mount\n}, [])\n```\n\n---\n\n### 🔹 Component Naming Conventions\n\n- **Use `PascalCase` for component names.**\n\n```tsx\nexport const ProfileHero = () =\u003e \u003cdiv\u003eProfile Hero\u003c/div\u003e\n```\n\n- **Use `camelCase` for non-component functions.**\n\n```tsx\nconst getProfileName = () =\u003e {}\n```\n\n---\n\n### 🔹 Loading \u0026 Empty State Components\n\n- **Loading states should mirror the component structure** but with skeleton placeholders.\n- **A `ProfileLoading.tsx` should match `Profile.tsx`** and replace dynamic content with skeletons.\n\n```tsx\nexport const Profile = () =\u003e (\n  \u003csection className='bg-red'\u003e\n    \u003cProfileHero /\u003e\n    \u003cdiv\u003e\n      \u003cProfileSidebar /\u003e\n      \u003cProfileContent /\u003e\n      \u003cButton\u003eClick me\u003c/Button\u003e\n    \u003c/div\u003e\n  \u003c/section\u003e\n)\n```\n\n```tsx\nexport const ProfileLoading = () =\u003e (\n  \u003csection className='bg-red'\u003e\n    \u003cProfileHeroLoading /\u003e\n    \u003cdiv\u003e\n      \u003cProfileSidebarLoading /\u003e\n      \u003cProfileContentLoading /\u003e\n      \u003cdiv className='h-12 w-20'\u003e\n        \u003cSkeleton variant='rounded' /\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/section\u003e\n)\n```\n\n---\n\n### 🔹 When to Split Components?\n\nA component should be **split into smaller components if:**\n\n- ✅ It exceeds **150 lines**.\n- ✅ It **handles multiple responsibilities** (e.g., UI and state logic).\n- ✅ It **contains deeply nested JSX**.\n- ✅ It **repeats similar JSX structures** that could be reused.\n\n---\n\n### 🔹 When to Use a `common/` Component?\n\n- **If a component is reused across multiple features**, move it to `common/components/`.\n- **If a component is only used within one feature, keep it inside that feature's folder.**\n\n✅ **Example (Feature-Specific Component)**\n\n```\npages/profile/common/ProfileHero.tsx\n```\n\n✅ **Example (Shared Component)**\n\n```\ncommon/components/ImageWithFallback.tsx\n```\n\n---\n\n#### 🔹 Why This Works\n\n- ✅ **Keeps component logic predictable and structured.**\n- ✅ **Encourages clean, readable JSX formatting.**\n- ✅ **Prevents unnecessarily large components.**\n- ✅ **Standardizes naming and file placement across the codebase.**\n\n---\n\n## ⚡ Functions \u0026 Utilities\n\nThis section defines **where and how utility functions should be structured** to ensure **readability and\nmaintainability**.\n\n---\n\n### 🔹 Utility Function Placement\n\n- **Feature-specific utilities** should be inside a feature’s folder.\n- **Shared utilities across multiple features** should be moved to `constants/featureUtils.ts`.\n\n✅ **Example: Utility Function Placement**\n\n```\npages/profile/profileUtils.ts # Feature-specific utilities\nconstants/userUtils.ts # Shared utilities across features\n```\n\n✅ **Example: Exporting Multiple Utilities**\n\n```ts\nconst getProfileAvatar = () =\u003e {}\n\nconst getProfileName = () =\u003e {}\n\nexport { getProfileAvatar, getProfileName }\n```\n\n---\n\n### 🔹 Formatting Rules for Functions\n\n- **Avoid unnecessary function nesting** → Functions should be **flat and readable**, avoiding deeply nested logic.\n\n❌ **Bad Example (Unnecessary Nesting)**\n\n```ts\nconst getUserDetails = user =\u003e {\n  if (user) {\n    return {\n      id: user.id,\n      name: user.name,\n      email: user.email,\n    }\n  } else {\n    return null\n  }\n}\n```\n\n✅ **Good Example (Flat and Readable)**\n\n```ts\nconst getUserDetails = user =\u003e {\n  if (!user) return null\n\n  return {\n    id: user.id,\n    name: user.name,\n    email: user.email,\n  }\n}\n```\n\n---\n\n### 🔹 Return Placement in Functions\n\nReturn statements inside functions follow **consistent spacing rules** for readability.\n\n#### ✅ General Rules\n\n- **If an `if` statement is at the start of the function, do not add a blank line before it.**\n- **If an `if` statement appears in the middle of the function, add a blank line before it.**\n- **If an `if` statement contains multiple lines, place the `return` on its own line.**\n- **Single-line early returns should remain inline unless additional logic is present.**\n- **Do not add an extra blank line before the final return in a function.**\n\n---\n\n**✅ Example: Early return at the start of the function (no blank line)**\n\n```ts\nconst getProfileRole = (profileData: ProfileData) =\u003e {\n  if (!profileData?.id) {\n    console.warn('Profile data is missing ID')\n    return 'Guest'\n  }\n\n  return profileData.role\n}\n```\n\n**✅ Example: Single-line early return (no extra space needed)**\n\n```ts\nconst getProfileRole = (profileData: ProfileData) =\u003e {\n  if (!profileData?.id) return 'Guest'\n\n  return profileData.role\n}\n```\n\n**✅ Example: Returning directly in a function with no logic**\n\n```ts\nconst getProfileName = (profileData: ProfileData) =\u003e `${profileData.firstName} ${profileData.lastName}`\n```\n\n**✅ Example: if appears in the middle of the function (needs a blank line before it)**\n\n```ts\nconst getProfileName = (profileData: ProfileData) =\u003e {\n  const { firstName, lastName } = profileData ?? {}\n\n  if (!firstName || !lastName) return 'Guest'\n\n  return `${firstName} ${lastName}`\n}\n```\n\n**❌ Example: Missing space before if when it’s in the middle of the function**\n\n```ts\nconst getProfileName = (profileData: ProfileData) =\u003e {\n  const { firstName, lastName } = profileData ?? {}\n  if (!firstName || !lastName) return 'Guest'\n\n  return `${firstName} ${lastName}`\n}\n```\n\n**❌ Example: Extra blank line before a return when it’s the only statement**\n\n```ts\nconst getProfileName = (profileData: ProfileData) =\u003e {\n\n  return `${profileData.firstName} ${profileData.lastName}`\n}\n```\n\n**❌ Example: Extra blank line before an early return at the start of a function**\n\n```ts\nconst getProfileName = (profileData: ProfileData) =\u003e {\n\n  if (!firstName || !lastName) return 'Guest'\n\n  return `${firstName} ${lastName}`\n}\n```\n\n**❌ Example: Single-line early return should stay inline**\n\n```ts\nconst getProfileRole = (profileData: ProfileData) =\u003e {\n  if (!profileData?.id) { return 'Guest' }\n}\n```\n\n```ts\nconst getProfileRole = (profileData: ProfileData) =\u003e {\n  if (!profileData?.id) {\n    return 'Guest'\n  }\n}\n```\n\n---\n\n### 🔹 Summary of Return Placement Rules\n\n| Case                                         | Blank Line Before Return? |\n| -------------------------------------------- | ------------------------- |\n| Single return as the only function statement | ❌ No                     |\n| Early return at the start of a function      | ❌ No                     |\n| `if` appears in the middle of the function   | ✅ Yes                    |\n| Final return in a function                   | ❌ No                     |\n| Return inside a multi-line `if` block        | ✅ Yes                    |\n\n---\n\n#### 🔥 Final Thoughts\n\nReturn placement follows the same logic as variables:\n\n- **If an `if` appears in the middle of a function, add a blank line before it.**\n- **If an `if` is at the start of a function, no blank line is needed.**\n- **A multi-line `if` block always places the return on its own line.**\n- **A single-line early return remains inline unless there’s additional logic.**\n\nBy keeping returns structured and predictable, code stays **clean, readable, and consistent** across the project. 🚀\n\n---\n\n#### 🔹 Why This Works\n\n- ✅ **Keeps the focus on utility function placement \u0026 formatting.**\n- ✅ **Removes redundancy with Component Structure.**\n- ✅ **Ensures consistent utility function placement across the project.**\n\n---\n\n## 📡 GraphQL Queries\n\nA structured approach to handling GraphQL queries and mutations ensures readability, maintainability, and consistency\nacross the application.\n\n### 🔹 General Rules for GraphQL Queries \u0026 Mutations\n\n- **Queries \u0026 Mutations should be placed in `hooks/` inside their respective feature folder**\n\n**✅ Example:**\n\n```plaintext\nsrc/pages/profile/hooks/useGetProfileQuery.ts # Feature-specific query\nsrc/pages/profile/hooks/useCreateProfileMutation.ts # Feature-specific mutation\nsrc/hooks/useGetPredefinedGuideTagsQuery.ts # Sitewide query (used across features)\n```\n\n- **Use camelCase for variables** inside GraphQL operations to maintain consistency with JavaScript/TypeScript naming\n  conventions.\n- **Operation name should be based on the data being fetched/updated, ensuring consistency with file \u0026 function names.**\n\n**✅ Example:**\n\n```ts\nquery GetProfileQueryInProfile($id: ID!) { ... }\n```\n\n- **Sort fields alphabetically**, except for `id`, which should always be listed first as the primary identifier for\n  consistency and quick reference.\n- **GraphQL fields should match the query name** for clarity.\n- **For sitewide queries**, the operation name should remain generic and should not include `In{featureName}`.\n\n---\n\n### 🔹 Feature-Based Queries vs. Sitewide Queries\n\nTo differentiate feature-specific GraphQL queries/mutations from global queries, we use a structured naming convention:\n\n#### Feature-Based Queries \u0026 Mutations\n\n- **Feature-specific queries \u0026 mutations** should include `In{featureName}` in the operation name to differentiate them\n  from sitewide queries and avoid naming conflicts.\n- **File Placement:** Should be placed within the feature folder inside `pages/featureName/`.\n\n**✅ Example:**\n\n```plaintext\nsrc/pages/profile/hooks/useGetProfileQuery.ts # Query used only in Profile\nsrc/pages/profile/hooks/useUpdateProfileMutation.ts # Mutation used only in Profile\n```\n\n**✅ Query Example:**\n\n```ts\nquery GetProfileQueryInProfile($id: ID!) {\n  node(id: $id) {\n    ... on Profile {\n      id\n      accountHandle\n      displayName\n      image\n    }\n  }\n}\n```\n\n#### Sitewide Queries \u0026 Mutations\n\n- Queries that **are used across multiple features** should **not include the feature name** in their operation.\n- **File Placement:** These should be placed in `src/hooks/`.\n\n**✅ Example:**\n\n```plaintext\nsrc/hooks/useGetPredefinedGuideTagsQuery.ts # Sitewide query\n```\n\n**✅ Query Example:**\n\n```ts\nquery GetPredefinedGuideTags {\n  predefinedGuideTags {\n    id\n    name\n  }\n}\n```\n\n---\n\n#### 🔹 Why This Naming Works\n\n- **Feature-Based Queries Include Feature Name**\n  - Queries scoped to a feature include `In{featureName}` (e.g., `GetProfileQueryInProfile`) to **prevent name\n    collisions**.\n  - This ensures clarity when multiple queries exist under the same feature.\n- **Sitewide Queries Should Remain Generic**\n  - If a query is used across multiple features, it should not include the feature name.\n  - This prevents unnecessary feature-specific naming for shared resources.\n- **Why We Avoid “QueryQuery”**\n  - If a query is called `GetPredefinedGuideTagsQuery`, the auto-generated type would be\n    `GetPredefinedGuideTagsQueryQuery`, which is **redundant**.\n  - By naming the file useGetPredefinedGuideTagsQuery.ts and using the operation name GetPredefinedGuideTags, we **avoid\n    the unnecessary duplication**.\n\n📌 Key Takeaways:\n\n- **If a query/mutation belongs to a single feature**, its operation should include the feature name (e.g.,\n  `GetProfileQueryInProfile`, `UpdateProfileMutationInProfile`).\n- **If a query/mutation is used across multiple features**, its operation name should not include the feature name\n  (e.g., `GetPredefinedGuideTags`, `UpdateUserSettingsMutation`).\n- **Feature-based queries \u0026 mutations should be placed inside `pages/featureName/`.**\n- **Sitewide queries \u0026 mutations should be placed in `src/hooks/`.**\n- **Mutations should always include ‘Mutation’ in both the GraphQL operation name and the filename (e.g.,\n  `useUpdateProfileMutation.ts`). Feature-based mutations follow the same `In{featureName}` rule as queries unless they\n  are sitewide.**\n  - ✅ Example: `useUpdateProfileMutation.ts`\n- **Feature mutations follow the same naming rule as feature queries, including `In{featureName}` unless they are\n  sitewide.**\n- **We avoid “QueryQuery” in auto-generated types by keeping the operation name clean.**\n- **We use PascalCase for hook return types, following `Use{QueryName}Return` (e.g., `UseGetProfileQueryReturn`).**\n\n---\n\n### 🔹 Example: Query for Fetching Profile Data\n\n```ts\nimport { gql, useQuery } from '@apollo/client'\n\ntype UseGetProfileQueryReturn = {\n  hasError: ApolloError\n  isLoading: boolean\n  profileData: Extract\u003c\n    GetProfileQueryInProfileQuery['node'],\n    {\n      __typename?: 'Profile'\n    }\n  \u003e\n}\n\nconst profileQuery = gql(`\n  query GetProfileQueryInProfile($id: ID!) {\n    node (id: $id) {\n      ... on Profile {\n        id\n        accountHandle\n        displayName\n        image\n      }\n    }\n  }\n`)\n\nexport const useGetProfileQuery = (id: string): UseGetProfileQueryReturn =\u003e {\n  const {\n    data,\n    error: hasError,\n    loading: isLoading,\n  } = useQuery(profileQuery, {\n    variables: {\n      id,\n    },\n  })\n\n  return {\n    hasError,\n    isLoading,\n    profileData: data?.node,\n  }\n}\n```\n\n#### 🔹 Why This Works\n\n- ✅ **CamelCase is used for variables** (accountHandle, displayName, etc.).\n- ✅ **id is prominently placed at the top** for consistency.\n- ✅ **Query follows a predictable naming structure** (`GetProfileQueryInProfile`).\n- ✅ **Custom hook abstracts error and loading states** for better readability (`hasError`, `isLoading`).\n\n---\n\n### 🔹 Example: Mutation for Updating Profile\n\n```ts\nimport { gql, useMutation } from '@apollo/client'\n\nconst updateProfileMutation = gql(`\n  mutation UpdateProfileMutationInProfile($updateProfileInput: UpdateProfileInput!) {\n    updateProfile(updateProfileInput: $updateProfileInput) {\n      id\n      displayName\n    }\n  }\n`)\n\nexport const useUpdateProfileMutation = () =\u003e useMutation(updateProfileMutation)\n```\n\n```tsx\nexport const ProfileForm = () =\u003e {\n  const [updateProfile, updateProfileResult] = useUpdateProfileMutation()\n\n  const onSubmit = async (id: string, displayName: string) =\u003e {\n    try {\n      await updateProfile({\n        variables: {\n          updateProfileInput: {\n            displayName,\n            id,\n          },\n        },\n      })\n    } catch (error) {\n      console.error('Failed to update profile', error)\n    }\n  }\n\n  return (\n    \u003cform onSubmit={onSubmit}\u003e\n      \u003cbutton type='submit'\u003eUpdate Profile\u003c/button\u003e\n    \u003c/form\u003e\n  )\n}\n```\n\n#### 🔹 Why This Works\n\n- ✅ **Mutation follows the naming pattern** (UpdateProfileMutationInProfile).\n- ✅ **Refetching the profile query ensures UI consistency**.\n- ✅ **Error and loading states are aliased as hasError and isLoading** for better readability.\n\n---\n\n## 🚩 Feature Flags\n\nFeature flags enable us to **conditionally enable or disable features** without deploying new code. This approach allows\nfor **progressive rollouts**, **A/B testing**, and **safe feature releases**.\n\n### 🔹 General Structure\n\nFeature flags are managed using **two primary components**:\n\n1. **Feature Flags Configuration (`featureFlags.ts`)**\n\n   - This file defines all **available feature flags**.\n   - Flags are stored as a **record of boolean values**.\n\n2. **Feature Flag Hook (`useFlag.ts`)**\n   - A **custom hook** to read feature flag values.\n   - Uses **local storage overrides**, allowing developers to toggle features locally.\n\n---\n\n### 📂 File Structure\n\n```plaintext\nsrc/\n├── config/\n│   ├── feature-flags/\n│   │   ├── featureFlags.ts    # Central feature flag configuration\n├── common/\n│   ├── hooks/\n│   │   ├── useFlag.ts         # Hook to check feature flag status\n```\n\n---\n\n### 🔹 Feature Flags Configuration\n\nFeature flags are **centrally defined** in `src/config/feature-flags/featureFlags.ts`. This ensures all available flags\nare explicitly listed.\n\n**✅ Example: Defining Feature Flags**\n\n```ts\n// src/config/feature-flags/featureFlags.ts\ntype FeatureFlagNames = 'profileHeroV2' | 'profileV2'\n\nconst featureFlags: Record\u003cFeatureFlagNames, boolean\u003e = {\n  profileHeroV2: false,\n  profileV2: false,\n}\n\nexport type { FeatureFlagNames }\nexport { featureFlags }\n```\n\n---\n\n### 🔹 Accessing Feature Flags with useFlag\n\nThe useFlag hook retrieves the current state of a feature flag, checking for local storage overrides.\n\n**✅ Example: Feature Flag Hook**\n\n```ts\n// src/common/hooks/useFlag.ts\nimport { useState, useEffect } from 'react'\nimport type { FeatureFlagNames } from 'src/config/feature-flags/featureFlags'\nimport { useLocalStorageFlags } from './useLocalStorageFlags'\n\nexport const useFlag = (flagKey: FeatureFlagNames | string): boolean =\u003e {\n  const [isFlagEnabled, setIsFlagEnabled] = useState(false)\n  const [localFlags] = useLocalStorageFlags()\n\n  useEffect(() =\u003e {\n    if (flagKey in localFlags) {\n      const { [flagKey]: localStorageFlag } = localFlags\n      setIsFlagEnabled(String(localStorageFlag).toLowerCase() === 'true')\n    }\n  }, [flagKey, localFlags])\n\n  return isFlagEnabled\n}\n```\n\n---\n\n### 🔹 Using Feature Flags in Components\n\n**✅ Example: Conditionally Rendering Components**\n\nFeature flags allow conditional rendering of components within a section.\n\n```tsx\nimport { useFlag } from 'src/common/hooks/useFlag'\nimport { ProfileHero } from './ProfileHero'\nimport { ProfileHeroOld } from './ProfileHeroOld'\n\nexport const Profile = () =\u003e {\n  const isProfileHeroV2Enabled = useFlag('profileHeroV2')\n\n  return (\n    \u003csection\u003e\n      {isProfileHeroV2Enabled ? \u003cProfileHero /\u003e : \u003cProfileHeroOld /\u003e}\n    \u003c/section\u003e\n  )\n}\n```\n\n---\n\n### 🔹 Using Feature Flags for Route-Based Feature Toggles\n\nFor **larger changes**, such as enabling an entirely new Profile redesign, we rename the existing feature folder\n(profile) to `profile-old` and introduce a new `profile/` folder.\n\nThen, in `PageRoutes.tsx`, we dynamically choose which version of `Profile` to render based on the feature flag.\n\n**✅ Example: Routing Feature Flag Usage**\n\n```tsx\nimport { useFlag } from 'src/common/hooks/useFlag'\nimport { Routes, Route } from 'react-router-dom'\nimport { Home } from 'src/pages/home'\nimport { Profile } from 'src/pages/profile'\nimport { ProfileOld } from 'src/pages/profile-old'\n\nexport const PageRoutes = () =\u003e {\n  const isProfileV2Enabled = useFlag('profileV2')\n\n  return (\n    \u003cScrollToTop\u003e\n      \u003cRoutes\u003e\n        \u003cRoute element={\u003cHome /\u003e} path='/' /\u003e\n        \u003cRoute\n          element={isProfileV2Enabled ? \u003cProfile /\u003e : \u003cProfileOld /\u003e}\n          path='/profile/:accountHandle'\n        /\u003e\n      \u003c/Routes\u003e\n    \u003c/ScrollToTop\u003e\n  )\n}\n```\n\n---\n\n### 🔹 Feature Flag Guidelines\n\n- **Feature flags should be short-lived**\n  - Avoid leaving feature flags in the codebase for an extended period.\n  - Flags should be **removed once the feature is stable**.\n- **New feature flags must be added to featureFlags.ts**\n  - This ensures **visibility** and prevents unexpected feature toggles.\n- **Use feature flags only for meaningful toggles**\n  - Feature flags should be reserved for **substantial feature rollouts, experiments, or redesigns**—not minor UI tweaks.\n- **Local storage overrides take precedence**\n  - Developers can **manually toggle flags via local storage**, making testing easier.\n\n---\n\n### ✅ Summary\n\n- **Feature flags are stored in `src/config/feature-flags/featureFlags.ts`.**\n- **The `useFlag` hook checks feature flag values, including local storage overrides.**\n- **Flags can be used for component toggles (`ProfileHeroV2`) or route-based toggles (`ProfileV2`).**\n- **Short-lived flags should be cleaned up after rollout is complete.**\n\n---\n\n## 🔠 Types \u0026 Interfaces\n\nA **consistent approach** to defining types and interfaces ensures **clarity, maintainability, and flexibility** across\nthe codebase.\n\n---\n\n### 🔹 General Rules\n\n- **Use `interface` for functional components.**\n\n  - Interfaces provide better readability when defining **props** for components.\n  - Ensures a **clear contract** for component usage.\n  - Supports **extending** when needed.\n\n- **Use `type` for everything else.**\n\n  - `type` provides better flexibility, particularly when defining **utility types, hooks, function return values, and\n    GraphQL queries**.\n  - Use `Extract\u003c\u003e` when working with **GraphQL queries that return multiple types**, ensuring type safety while\n    extracting a **specific expected type** from a union.\n\n- **Use `Pick\u003c\u003e` and `Omit\u003c\u003e` to create subsets of types.**\n\n  - `Pick\u003c\u003e` is used when selecting **only specific properties** from a type.\n  - `Omit\u003c\u003e` is used when removing **specific properties** from a type.\n\n---\n\n### 🔹 Component Props: Use `interface`\n\n✅ **Example: Functional Component Props**\n\n```tsx\ninterface ProfileHeroProps {\n  onClick: () =\u003e void\n  title: string\n}\n\nexport const ProfileHero = ({ onClick, title }: ProfileHeroProps) =\u003e (\n  \u003cdiv onClick={onClick}\u003e{title}\u003c/div\u003e\n)\n```\n\n**✅ Example: Extending an Interface**\n\nUse `interface` to extend props cleanly, while type uses `\u0026` for merging multiple types.\n\n```tsx\nimport { Button } from '@travelpass/design-system'\nimport type { GenericAddress } from 'src/__generated__/graphql'\n\ninterface ProfileAddressProps extends GenericAddress {\n  onClick: VoidFunction\n}\n\nexport const ProfileAddress = ({\n  addressLine1,\n  city,\n  country,\n  onClick,\n}: ProfileAddressProps) =\u003e (\n  \u003csection\u003e\n    \u003ch2\u003e{name}\u003c/h2\u003e\n    \u003cp\u003e{getAddress(addressLine1, city, country)}\u003c/p\u003e\n    \u003cButton onClick={onClick}\u003eEdit\u003c/Button\u003e\n  \u003c/section\u003e\n)\n```\n\n---\n\n### 🔹 Utility Types: Use `type`\n\nUse `Pick\u003c\u003e` when selecting only specific properties from a type, and `Omit\u003c\u003e` when removing specific properties.\n\nThese help create lightweight, flexible types for better reusability.\n\n**✅ Example: Utility Type for Query Results**\n\n```ts\ntype UseGetProfileQueryReturn = {\n  hasError: ApolloError\n  isLoading: boolean\n  profileData: Extract\u003c\n    GetProfileQueryInProfileQuery['node'],\n    {\n      __typename?: 'Profile'\n    }\n  \u003e\n}\n```\n\n**✅ Example: Extracting Only Specific Keys from an Object**\n\n```ts\ntype UserKeys = 'id' | 'email'\n\ntype UserInfo = Pick\u003cUser, UserKeys\u003e\n```\n\n**✅ Example: Omitting Unnecessary Fields from an Object**\n\n```ts\ntype User = {\n  id: string\n  email: string\n  password: string\n}\n\ntype PublicUser = Omit\u003cUser, 'password'\u003e\n```\n\n**✅ Example: Combining Multiple Types**\n\nUse `\u0026` to merge multiple types, providing more flexibility than `interface` extension.\n\n```ts\ntype Base = {\n  createdAt: string\n}\n\ntype Profile = {\n  id: string\n  name: string\n}\n\ntype ProfileWithBase = Profile \u0026 Base\n```\n\n---\n\n### 🔹 When to Use `Extract\u003c\u003e` in GraphQL\n\n- Use `Extract\u003c\u003e` to ensure type safety when selecting a specific type from a GraphQL query result.\n\n**✅ Example: Extracting the Profile Type from a Query**\n\n```ts\ntype UseGetProfileQueryReturn = {\n  hasError: ApolloError\n  isLoading: boolean\n  profileData: Extract\u003c\n    GetProfileQueryInProfileQuery['node'],\n    {\n      __typename?: 'Profile'\n    }\n  \u003e\n}\n```\n\n---\n\n### 🔹 Avoid Unnecessary interface Usage\n\n**❌ Bad Example: Using interface for Utility Types**\n\n```ts\ninterface UseGetProfileQueryReturn {\n  hasError: ApolloError\n  isLoading: boolean\n  profileData: Profile\n}\n```\n\n**✅ Good Example: Using type for Flexibility**\n\n```ts\ntype UseGetProfileQueryReturn = {\n  hasError: ApolloError\n  isLoading: boolean\n  profileData: Profile\n}\n```\n\n---\n\n### ✅ Summary\n\n- **Use interface for defining props in functional components.**\n- **Use type for everything else (utilities, hooks, GraphQL queries, API responses).**\n- **Use `Extract\u003c\u003e` to ensure type safety when narrowing GraphQL query results.**\n- **Keep types minimal and readable—avoid unnecessary abstractions.**\n\n---\n\n## 📝 Comments \u0026 Documentation\n\nA **minimalist approach** to comments ensures code is **clean, readable, and self-explanatory**. Instead of excessive\ncommenting, we prioritize **descriptive function and variable names**. Comments are used **only when necessary**, such\nas for **complex logic, workarounds, or TODOs**.\n\n---\n\n### 🔹 General Rules\n\n- **Favor meaningful variable and function names over comments.**\n\n  - Code should **explain itself** rather than rely on comments.\n  - If logic is unclear, **refactor instead of adding a comment**.\n\n- **Use JSDoc (`@see`) when the workaround requires a reference link, external documentation, or detailed\n  explanation.**\n\n  - JSDoc ensures proper referencing in documentation tools like TypeDoc.\n  - Example:\n    ```ts\n    /**\n     * Safari requires a slight delay for smooth scrolling.\n     * @see https://stackoverflow.com/q/xxxx\n     */\n    ```\n\n- **Use JSDoc (`@todo`) for marking future work.**\n\n  - We use `@todo` in JSDoc **sparingly** for tracking unfinished tasks.\n  - If a task has a corresponding Linear issue, consider referencing it using `@todo Linear-123`.\n    - Example: `/** @todo TMNT-123 Update this when the new API version is available */`\n  - JSDoc comments should be **above the function or logic they reference**.\n  - Prefer **compact, one-line comments** whenever possible.\n\n- **Use inline `//` comments for workarounds or technical limitations.**\n\n  - These should be **short** and placed **directly above the relevant line**.\n\n- **Avoid excessive commenting.**\n  - **Only document \"why\"** something is done, not **\"what\"** the code does.\n\n---\n\n### 🔹 Using JSDoc for TODOs\n\nWe **only** use JSDoc (`/** @todo */`) for tracking future work.\n\n**✅ Example: JSDoc TODO for Future Enhancements**\n\n```ts\n/** @todo Update this when the new API version is available */\nconst getUserPreferences = async (userId: string) =\u003e {\n  try {\n    return await fetch(`/api/preferences/${userId}`)\n  } catch (error) {\n    console.error(error)\n    return null\n  }\n}\n```\n\n**❌ Avoid Unnecessary TODO Comments**\n\nThis format is not compatible with JSDoc linters.\n\n```ts\n// @todo Update this when the new API version is available\nconst getUserPreferences = async (userId: string) =\u003e {\n  try {\n    return await fetch(`/api/preferences/${userId}`)\n  } catch (error) {\n    console.error(error)\n    return null\n  }\n}\n```\n\n💡 Key Difference:\n\n- **✅ Use `@todo` for JSDoc-style TODOs.**\n- **❌ Avoid inline `// @todo` comments.**\n\nJSDoc is more structured and aligns with tools that scan TODOs.\n\n---\n\n### 🔹 Inline Comments for Workarounds\n\nUse inline `//` comments for technical workarounds, browser quirks, or unexpected API behavior.\n\n**✅ Example: Workaround for Safari Quirk**\n\n```ts\nconst scrollToTop = () =\u003e {\n  window.scrollTo(0, 0)\n  // Safari requires a slight delay for smooth scrolling\n  setTimeout(() =\u003e window.scrollTo(0, 0), 10)\n}\n```\n\n**✅ Example: Workaround for Safari Quirk with `@see`**\n\n```ts\n/**\n * Safari requires a slight delay for smooth scrolling.\n * @see https://stackoverflow.com/q/xxxx\n */\nconst scrollToTop = () =\u003e {\n  window.scrollTo(0, 0)\n  setTimeout(() =\u003e window.scrollTo(0, 0), 10)\n}\n```\n\n**❌ Avoid Redundant Comments**\n\n```ts\nconst scrollToTop = () =\u003e {\n  // Scrolls to the top of the page\n  window.scrollTo(0, 0)\n}\n```\n\n💡 Key Difference:\n\n- **✅ Comments should explain “why” a workaround is needed.**\n- **❌ Avoid stating what the code already makes obvious.**\n\n---\n\n### 🔹 Handling Complex useEffect Hooks\n\nFor `useEffect`, prefer extracting logic into functions instead of writing comments inline.\n\n**✅ Example: Extracting Logic Into a Function**\n\n```ts\nuseEffect(() =\u003e {\n  syncUserPreferences()\n}, [])\n\nconst syncUserPreferences = async () =\u003e {\n  try {\n    /** @todo Remove this workaround when the API provides real-time updates */\n    const preferences = await getUserPreferences(user.id)\n    applyUserPreferences(preferences)\n  } catch (error) {\n    console.error(error)\n  }\n}\n```\n\n**❌ Example of an Overloaded useEffect with Comments**\n\n```ts\nuseEffect(() =\u003e {\n  // Fetch user preferences and apply them\n  fetch(`/api/preferences/${user.id}`)\n    .then(res =\u003e res.json())\n    .then(preferences =\u003e {\n      // Apply user preferences\n      applyUserPreferences(preferences)\n    })\n}, [])\n```\n\n💡 Key Takeaway:\n\n- **✅ Extract logic into functions rather than writing inline comments in `useEffect`.**\n- **❌ Long comments inside `useEffect` add clutter.**\n\n### 🔹 When to Write a Comment vs. Refactor?\n\nBefore writing a comment, ask:\n\n- **Is the function name clear enough?**\n  - If **no**, rename the function instead of adding a comment.\n- **Is this logic unavoidable or non-obvious?**\n  - If **yes**, add an inline comment.\n- **Is this a workaround for a browser quirk or API limitation?**\n  - If **yes**, a comment is useful.\n\n---\n\n### ✅ Summary\n\n- **Avoid unnecessary comments—favor meaningful variable \u0026 function names.**\n- **Use JSDoc `@todo` for tracking future work.**\n- **Use inline `//` comments only for workarounds or unexpected behavior.**\n- **Refactor first, comment as a last resort.**\n- **If a `useEffect` is complex, extract logic into functions instead of writing comments.**\n\n---\n\n## 🤝 Contributing\n\nThank you for considering contributing to this project! We appreciate your help in improving and maintaining this\nrepository.\n\n---\n\n### 🔹 How to Contribute\n\n1. **Fork the Repository**\n\n   - Click the **Fork** button on the top right of this repository.\n   - This will create a copy under your GitHub account.\n\n2. **Clone Your Fork**\n\n   - Run the following command to clone the forked repository:\n\n     ```bash\n     git clone https://github.com/YOUR-USERNAME/react-typescript-style-guide.git\n     cd react-typescript-style-guide\n     ```\n\n3. **Make your changes in `main`**\n\n   - Open the project in your preferred editor.\n   - Make your changes while following the project’s coding guidelines.\n\n4. **Commit your changes**\n\n   ```bash\n   git add .\n   git commit -m \"Describe your changes\"\n   ```\n\n5. **Push to your fork**\n\n   ```bash\n   git push origin main\n   ```\n\n6. **Create a Pull Request**\n   - Go to the **original repository** on GitHub.\n   - Click **Compare \u0026 pull request**.\n   - Add a **clear description** of your changes.\n   - Click **Create pull request**.\n\n---\n\n### 🔹 Contribution Guidelines\n\n- **Keep PRs small and focused**\n\n  - If your change is large, consider breaking it into smaller PRs.\n\n- **Follow the existing code style**\n\n  - Ensure your code follows formatting and linting rules.\n\n- **Write clear commit messages**\n\n  - Use concise descriptions like `Fix button alignment issue` or `Add support for dark mode`.\n\n- **Ensure your changes do not break existing functionality**\n\n  - Test your changes before submitting.\n\n- **Be respectful and collaborative**\n  - We appreciate all contributions and encourage constructive feedback.\n\n---\n\n✅ **Thank you for contributing! We appreciate your support in improving this project.** 🚀\n\n---\n\n## 📜 License\n\nThis project is licensed under the **MIT License**.\n\nYou are **free to use, modify, distribute, and share** this project with no restrictions, as long as the original\nlicense and copyright notice are included.\n\n### 📄 Full License\n\nThe full license text is available in the [`LICENSE.md`](./LICENSE.md) file.\n\n---\n\n## 📚 References \u0026 Inspirations\n\nThis style guide follows **widely accepted industry standards** while maintaining a **minimal, structured, and\nopinionated** approach. Below are key resources that **align with and support the philosophy, structure, and best\npractices outlined in this guide**.\n\n### **📌 Key Influences on This Guide**\n\nEach of the following references **shares core principles** with this style guide, such as **clarity, maintainability,\npredictability, and reducing complexity**.\n\n| Reference                                 | Link                                                                           | How It Relates                                                                                                                                                                                                   |\n| ----------------------------------------- | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Google TypeScript Style Guide**         | [Google TypeScript Guide](https://google.github.io/styleguide/tsguide.html)    | ✅ **Readability \u0026 maintainability** via **consistent naming, structured function ordering, and predictable patterns**.\u003cbr\u003e✅ Aligns with **Component Order** and **Separation of Concerns** principles.         |\n| **Airbnb React/JSX Style Guide**          | [Airbnb React Guide](https://airbnb.io/javascript/react/)                      | ✅ Focuses on **self-contained components, logical function ordering, and clean JSX formatting**.\u003cbr\u003e✅ Strongly aligns with **Component Structure**—especially **hooks, variables, and function organization**. |\n| **Shopify JavaScript \u0026 TypeScript Guide** | [Shopify JavaScript \u0026 TypeScript Guide](https://github.com/Shopify/javascript) | ✅ Encourages **feature-based folder structure**, aligning with **Folder Structure**.\u003cbr\u003e✅ Supports **encapsulating GraphQL queries within feature folders**, similar to our **GraphQL Queries** section.       |\n| **TS.dev TypeScript Style Guide**         | [TS.dev Guide](https://ts.dev/style/)                                          | ✅ Emphasizes **clarity and minimalism**, reinforcing **No Unnecessary Abstraction**.\u003cbr\u003e✅ Aligns with **using interfaces for components and types for utilities/hooks**.                                       |\n| **TypeScript Deep Dive Style Guide**      | [TypeScript Deep Dive](https://basarat.gitbook.io/typescript/styleguide)       | ✅ Advocates **predictable, structured code organization** and **explicit return types**.\u003cbr\u003e✅ Aligns with **Types \u0026 Interfaces**, particularly **Extract\u003c\u003e, Pick\u003c\u003e, and Omit\u003c\u003e usage**.                        |\n\n---\n\n### **💡 Final Thoughts**\n\nThis style guide follows **industry best practices** while taking a **minimalist approach** to ensure **scalability,\npredictability, and maintainability**.\n\nBy adopting these conventions, you **ensure consistency across projects** while writing **modern, well-structured\nReact + TypeScript code**.\n\n🚀 **Thank you for following this guide! Your contributions help keep codebases clean, readable, and scalable.**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmlane%2Freact-typescript-style-guide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmlane%2Freact-typescript-style-guide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmlane%2Freact-typescript-style-guide/lists"}