> For the complete documentation index, see [llms.txt](https://pavewise.gitbook.io/pavewise-style-guide-and-more/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://pavewise.gitbook.io/pavewise-style-guide-and-more/frontend/basic-guidelines/react.md).

# React

* Always use **JSX syntax** (vs React.createElement) [**\[1\]**](https://react.dev/reference/react/createElement#creating-an-element-without-jsx)[**\[2\]**](https://react.dev/learn/writing-markup-with-jsx)[**\[3\]**](https://legacy.reactjs.org/docs/introducing-jsx.html)[**\[4\]**](https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html#whats-a-jsx-transform)[**\[5\]**](https://github.com/reactjs/rfcs/blob/createlement-rfc/text/0000-create-element-changes.md#motivation)
* Always use React **functional components** (vs class components) [**\[1\]**](https://www.geeksforgeeks.org/differences-between-functional-components-and-class-components/#)[**\[2\]**](https://react.dev/learn/your-first-component#defining-a-component)

<details>

<summary>Notes (functional components)</summary>

~~Exceptions: When creating custom components that need to accept a ref, use a class component (function components cannot accept refs) (see example)~~ (in React 19, we will not need to do anything different for the "ref" prop)

</details>

* Use **function declaration** syntax for components and other functions (vs function expression)

<details>

<summary>Example (syntax -- function declaration vs function expression)</summary>

```tsx
// function declaration (uses 'function' keyword) (preferred)
interface Props {}

export function ComponentName({}: Props) {
  return (
    <>
      
    </>
  );
}


// function expression (uses 'const' & =>) (not preferred)
interface Props {}

export const ComponentName = ({  }: Props) => {
  return (
    <>
      
    </>
  );
};
```

</details>

* Use **named exports** for components and other functions/variables/etc. (vs default exports) [**\[1\]**](https://react.dev/learn/importing-and-exporting-components#exporting-and-importing-multiple-components-from-the-same-file)[**\[2\]**](https://stackoverflow.com/questions/46913851/why-and-when-to-use-default-export-over-named-exports-in-es6-modules)

<details>

<summary>Notes (exports -- named vs default)</summary>

Exceptions: `page.tsx` , `layout.tsx` (use default)

</details>

<details>

<summary>Example (exports -- named vs default)</summary>

```tsx
// named export (preferred) (no "default" keyword)
interface Props {}

export function ComponentName({}: Props) {
  return (
    <>
      
    </>
  );
}

// default export ("default" keyword)
interface Props {}

export default function ComponentName({}: Props) {
  return (
    <>
      
    </>
  );
}
```

</details>

* Use **function declaration/signature line exporting** (vs bottom of file)

<details>

<summary>Example (exports -- declaration/signature line vs bottom of file)</summary>

```tsx
// function declaration/signature line export (preferred)
export function ComponentName({}: Props) {
  ...
}

// bottom-of-file export (not preferred)
function ComponentName({}: Props) {
  ...
}
export { ComponentName }
```

</details>

* Only include **one exported component per file**.

<details>

<summary>Notes (one exported component per file)</summary>

* it is encouraged to abstract render details into their own "local components" (within same file or separate files within same "component folder") to keep the render code minimal, straightforward, and maintainable

</details>

<details>

<summary>Example (one exported component per file; local components)</summary>

```tsx
// functional component (also the "new component" template we use within Pavewise)

interface Props {}

export function ComponentName({}: Props) {
  return (
    <>
      { /* Add your code here */}
    </>
  );
}
```

```tsx
// component that has a "local component"
// still only exporting one component (the "main component")

interface Props {}

export function ComponentName({}: Props) {
  return (
    <>
      <LocalComponent />
      { /* Add your code here */}
    </>
  );
}

// local component (note that it is NOT exported -- only used in this file)
// (it is abstracted logic/UI from the main component for simplification/readability/maintainability purposes)
interface LocalComponentProps {}

function LocalComponent({}: LocalComponentProps) {
  return (
    <>
      { /* Add your code here */}
    </>
  );
}
```

</details>

* Use **interface Props {}** for typing main component props (vs named props or `type`) [**\[1\]**](https://github.com/microsoft/TypeScript/wiki/Performance#preferring-interfaces-over-intersections)

<details>

<summary>Notes (interface vs type)</summary>

* when combining multiple types that are objects, using `interface extends Type1, Type2` catches "same property name but imcompatable types", whereas `type = Type1 & Type2` does not

</details>

<details>

<summary>Example (interface Props)</summary>

```typescript
// interface, generic props (preferred)
interface Props {}

// interface, named props (not preferred)
interface ComponentNameProps {}

// type (not preferred)
type ComponentNameProps = {}
```

</details>

* Every component should exist within a **"component file" of the same name & casing**.
  * If local components, helper functions, etc. are extracted into their own files, then a **"component folder"** should be created and **everything should be placed within it** (including the component file)

<details>

<summary>Notes ("component file", "component folder")</summary>

**"Component folder":**  When local components, helper functions, etc. are extracted into their own files, a "component folder" should be created with the same name of the component and component file, and should contain (1) the main component file itself, (2) an `index.ts` file that simply exports that component, and (3) "\_x" folder(s) containing the extracted local components, helper functions, etc.

***

**"\_x" folders:**  For more complex components, it may make sense to extract logic/UI/etc. into their own local files (e.g. to prevent any single file from getting too big).\
\
In these situations, local files are to be places in local "\_x" folders:

* `_components` *(holds components)*
* `_lib` *(holds any non-component files/folders -- helpers, hooks, etc.)*

These extract components, helper functions, types, constants, etc. that are only used by the main component are termed "local" components, helper functions, etc., and should be co-located within the component folder".

***

</details>

<details>

<summary>Example ("component file", "component folder")</summary>

The (1) component folder, (2) component file, and (3) function signature should all have the same name, and an `index.ts` file should export the component file

***

**Example: Header component**

Header "component file" (no local component/function files)

![](/files/E8QKO98zJ2zLf09Jb6oo)

Header "component folder"

![](/files/1d5Qd3lvBmjIL1XRzkGp)

* 📄 **index.ts**: `` export * from './Header` `` *(only exports the main component)*
* 📁 **\_components**: local components
* 📁 **\_lib**: everything else (helper functions, hooks, context, etc)

![](/files/hAMjlFySEjrinAfuHjbc)

***

Example folder structures:

```tsx
// MINIMAL
~/components/.../Component/
--Component.tsx
--index.ts (simply exports Component.tsx)

// MINIMAL (with local components)
~/components/.../MainComponent/
--Component.tsx
--index.ts (simply exports Component.tsx)
--_components/
----LocalComponent1.tsx
----LocalComponent2.tsx
----LocalComponent3.tsx
----index.ts (exports all files within _components folder)

// MINIMAL (with local non-component folders/files) (e.g. helper functions, etc.)
~/components/.../Component/
--Component.tsx
--index.ts (simply exports Component.tsx)
--_lib/  (containing local types, helpers, hooks, etc.)
----_hooks/
------useLocalHook.tsx
------index.ts (exports all files within _hooks folder)
----_helpers/
------customLocalHelperFunction.tsx
------index.ts (exports all files within _helpers folder)
----index.ts (exports all folders/files within _lib folder)

// COMPLEX COMPONENT (with local components, helper functions, Storybook (testing) files, etc.)
~/components/.../Component/
--Component.tsx
--Component.stories.tsx  (StorybookJS)
--index.ts (simply exports Component.tsx)
--_components/
----...
----index.ts (exports all folders/files within _components folder)
--_lib/
----_hooks/
----_helpers/
----...
----index.ts (exports all folders/files within _lib folder)


```

</details>

* For conditional rendering, prefer **multiple returns (early returns)** (vs single return with internal conditional rendering of JSX) [**\[1\]**](https://twitter.com/i/bookmarks?post_id=1799640178459656662)
  * Multiple returns (early returns)
    * ✅  `if` conditions
  * Single return
    * ❌  `&&` (logical AND operator)
    * ❌  `? :` (ternary operator)

<details>

<summary>Example (conditional rendering -- multiple returns vs single return)</summary>

<pre class="language-tsx"><code class="lang-tsx"><strong>// Example: multiple returns (early returns) (preferred)
</strong>function Component({ isLoading, data }) {
  if (isLoading) return &#x3C;Loading />;
  if (data.length === 0) return &#x3C;NoData />;

  return &#x3C;SuccessReturn />;
}

// Example: single return (conditional rendering, using logical AND operator)
function Component({ isLoading, data }) {
  return (
    &#x3C;>
      {isLoading &#x26;&#x26; &#x3C;Loading />}
      {data.length === 0 &#x26;&#x26; &#x3C;NoData />}
      {data.length > 0 &#x26;&#x26; &#x3C;SuccessReturn />}
    &#x3C;/>
  );
}

// Example: single return (conditional rendering, using ternary operators)
function Component({ isLoading, data }) {
  return isLoading ? (
    &#x3C;Loading />
  ) : data.length === 0 ? (
    &#x3C;NoData />
  ) : (
    &#x3C;SuccessReturn />
  );
}
</code></pre>

![](/files/p29TNawQR1xE3hdTt2Wv)

</details>

* For event handlers:
  * use **inline anonymous functions (arrow syntax)** when the handling logic is minimal and straightforward (vs creating an extracted/named event handler function) [**\[1\]**](https://react.dev/learn/responding-to-events#naming-event-handler-props:~:text=Alternatively%2C%20you%20can%20define%20an%20event%20handler%20inline%20in%20the%20JSX)
  * use **handleX** syntax for extracted/named event handlers functions [**\[1\]**](https://react.dev/learn/responding-to-events#naming-event-handler-props:~:text=By%20convention%2C%20it%20is%20common%20to%20name%20event%20handlers)
    * **X** should be **consistent with the prop name** when possible (onClick prop = handleClick event handler)
  * use **onX** syntax for event handler props (component prop names that receive event handler functions) [**\[1\]**](https://react.dev/learn/responding-to-events)
  * use **e** when referring to event objects in event handler functions (vs event, etc.) [**\[1\]**](https://react.dev/learn/responding-to-events#stopping-propagation:~:text=By%20convention%2C%20it%E2%80%99s%20usually%20called%20e%2C%20which%20stands%20for%20%E2%80%9Cevent%E2%80%9D)

<details>

<summary>Notes (event handlers)</summary>

* **inline anonymous functions**
  * Goal: keep component body logic minimal (easier code navigation; less mental overhead)
* **X** should be **consistent with the prop name** when possible
  * If there is only a **single instance of a prop name** in a given component (which can often happen, with modular, [SRP](https://react.dev/learn/thinking-in-react#step-1-break-the-ui-into-a-component-hierarchy:~:text=single%20responsibility%20principle) components), **use it for the function name**
    * prop name: onClick
    * function name: handleClick
  * If there are **multiple instances of a prop name**, then follow the [**"NounAdjective"**](broken://pages/LI2VXX4bKPZpAcxZEVTU) convention (**append further specificity to the end of the name**), or use more specific/descriptive
    * handleClickX, handleClickY, ...
    * handleCreate, handleDelete, ...

</details>

<details>

<summary>Example (event handlers -- inline anonymous vs extracted named)</summary>

```tsx
// event handler: inline anonymous arrow function (preferred)
function Component() {
  const [state, setState] = useState();

  return <button onClick={() => setState('clicked')}>Click me</button>;
}

// event handler: extracted named function
// - only use when logic starts to become complex
function Component() {
  const [state, setState] = useState();

  function handleClick() {
    setState('clicked');
  }

  return <button onClick={handleClick}>Click me</button>;
}
```

</details>

* For (state) updater functions, use **prev** **or first letters of corresponding state variable** as the argument name [\[1\]](https://react.dev/learn/queueing-a-series-of-state-updates#naming-conventions)

<details>

<summary>Example (updater functions -- prev or first letters of state variable)</summary>

```tsx
// prev
setEnabled(prev => !prev);
setLastName(prev => prev.reverse());
setFriendCount(prev => prev & 2);

// first letters of corresponding state variable
setEnabled(e => !e);
setLastName(ln => ln.reverse());
setFriendCount(fc => fc * 2);
```

</details>

* If using reducers: [\[1\]](https://react.dev/learn/extracting-state-logic-into-a-reducer)
  * use a **switch statement** (vs if/else)
    * use `{` and `}` for each case when needing to declare local variables
  * use more **generic action strings** (vs state-specific names)

<details>

<summary>Example (reducers)</summary>

```tsx
// REDUCER LOGIC
// switch statement (preferred) (with {...} around each case)
function tasksReducer(tasks, action) {
  switch (action.type) {
    case 'added': {
      return [
        ...tasks,
        {
          id: action.id,
          text: action.text,
          done: false,
        },
      ];
    }
    case 'changed': {
      return tasks.map((t) => {
        if (t.id === action.task.id) {
          return action.task;
        } else {
          return t;
        }
      });
    }
    case 'deleted': {
      return tasks.filter((t) => t.id !== action.id);
    }
    default: {
      throw Error('Unknown action: ' + action.type);
    }
  }
}

// if/else statement
function tasksReducer(tasks, action) {
  if (action.type === 'added') {
    return [
      ...tasks,
      {
        id: action.id,
        text: action.text,
        done: false,
      },
    ];
  } else if (action.type === 'changed') {
    return tasks.map((t) => {
      if (t.id === action.task.id) {
        return action.task;
      } else {
        return t;
      }
    });
  } else if (action.type === 'deleted') {
    return tasks.filter((t) => t.id !== action.id);
  } else {
    throw Error('Unknown action: ' + action.type);
  }
}

// ACTION STRINGS
// generic (preferred)
// "added", "changed"/"updated", "deleted"

// state-specific
// "added_task", "changed_task"/"updated_task", "deleted_task"
```

</details>
