New Component

New component

Location and structure

The files and folder structure follows the tightly-coupled architecture (opens in a new tab) and underlies the Next.js file-system router (opens in a new tab). That means that a file and code should live as close as possible to its context. In the image below you can see we've a UserForm component that is located under app/[locale]/(main)/users/add/_components. It does have other components it uses. Because they're specific to it, they live in the same folder (or subdirectories of the /_components folder) and follow the same architecture.

New Component Structure

Details

General

  • the component is located in:
    • the _components folder
    • a folder that is located inside the same directory as the component that uses it
  • the file is named after the component and written in PascalCase

Specific

  • use functional components instead of class components
// ❌
class NewComponent extends PureComponent { ... }
 
// ✅
function NewComponent(...) { ... }
Why?

Functional components are easier to read and write. They're also more performant than class components. Additionally, we can make use of React Hooks.


  • import types with import type { ... } from ... syntax
// ❌
import { NewType } from ...
 
// ✅
import type { NewType } from ...

Import Cost Valid

Why?

It helps reducing the bundle size of your JavaScript code. The smaller the bundle size, the faster the page loads.


  • use function declaration syntax instead of arrow function syntax.
// ❌
const NewComponent = (...) => { ... }
 
// ✅
function NewComponent(...) { ... }
Why?

It's easier to read and identify a piece of code as function.


  • export the functional component as default export (export default MyComponent) and at the bottom of the file
// ❌
export function NewComponent(...) { ... }
 
// ✅
function NewComponent(...) { ... }
...
export default function NewComponent
Why?

Next.js needs a React component to be exported via export default.


  • type the component's props with a TypeScript type and destructure them where possible.
// ❌
function NewComponent(cmpProps: { username: string; age: number; }) { ... }
 
// ❌
function NewComponent({ username, age }: { username: string; age: number; }) { ... }
 
// ✅
type Props = {
  username: string
  age: number
}
 
// NewComponent.tsx
function NewComponent({ username, age }: Props) { ... }
Why?

Reduces the amount of code and increases the readability.


  • specify the return type of a component.
// ❌
function NewComponent(...) { ... }
 
// ✅
function NewComponent(...): JSX.Element { ... }
Why?

By specifying the concret return type, TypeScript will give you an error if you accidentally return a wrong type. Additionally, it increases the consistency of always specifying the return type of functions throughout the codebase and provides a better readability if you can directly recognize what a function returns.


  • declare types specific to a file inside the file at the top (like type Props)
Why?

  • logic that belongs together should live as close together as possible and they should be separated through a blank line.
// ❌
function NewComponent(...): JSX.Element {
  const [toggleColorTheme, setToggleColorTheme] = useState('light')
  const { t } = useTranslation()
  function onToggleColorTheme(): void {
    ...
    setToggleColorTheme('dark')
    ...
  }
}
 
// ✅
function NewComponent(...): JSX.Element {
  const { t } = useTranslation()
 
  const [toggleColorTheme, setToggleColorTheme] = useState('light')
 
  function onToggleColorTheme(): void {
    ...
    setToggleColorTheme('dark')
    ...
  }
}

  • Separate JSX elements with a blank line if they have the same indentation.
// ❌
function NewComponent(...): JSX.Element {
  return (
    <div>
      <div>
        <h2>Foo</h2>
      </div>
      <div>
        <h3>Bar</h3>
      </div>
    </div>
  )
}
 
// ✅
function NewComponent(...): JSX.Element {
  return (
    <div>
      <div>
        <h2>Foo</h2>
      </div>
 
      <div>
        <h3>Bar</h3>
      </div>
    </div>
  )
}

  • all texts are replaced with a corresponding i18n variable
// ❌
<h3>Login</h3>
 
// ✅
<h3>{t('login.title')}</h3>
Why?

To prevent hard coded strings inside the code and have only one source-of-truth for all your translations.


  • avoid any where possible
// ❌
function NewComponent(...): JSX.Element {
  const { t } = useTranslation()
 
  const [user, setUser] = useState<any>({ name: '', email: '' })
}
 
// ✅
type User = {
  name: string
  email: string
}
 
function NewComponent(...): JSX.Element {
  const { t } = useTranslation()
 
  const [user, setUser] = useState<User>({ name: '', email: '' })
}
Why?

By telling TypeScript that a variable or function is of type any you disable TypeScript for it and don't get any type-safety anymore. Some typing is hard, we all know that, but at least ask for help and don't go the simple and insecure way.


  • treat server requests as sensible
Why?

Double-check if you make unnecessary or too many server requests caused by a suboptimal implementation. It could led to a bad performance and UX.


  • provide feedback for every write, delete, update operation
Why?

A good UX consists of proper user feedback i.e. notifications. That gives the user confidence that his action was successful or has failed.


  • use proper code formatting via Prettier & linting via ESLint
Why?

Tools like Prettier (semantic) and ESLint (syntax) ensure a consistent codebase and keep readability on a high level.


  • check console (browser and terminal) for any errors or warnings
Why?

Even though your implementation is fine, a warning or even an error can occur. Sometimes they're not reflected in the UI but in the console.

Example

// NewComponent.tsx
import { User } from 'your-types-package'
 
type Props = {
  onSave: () => void
}
 
export function NewComponent({onSave}: Props): JSX.Element {
  const { t } = useTranslation()
 
  const [user, setUser] = useState<User>({ name: '', email: '' })
 
 
  const [toggleColorTheme, setToggleColorTheme] = useState('light')
 
  function onToggleColorTheme(): void {
    ...
    setToggleColorTheme('dark')
    ...
    onSave()
  }
 
    return (
    <div>
      <div>
        <h2>{t('newComponent.title')}</h2>
      </div>
 
      <div>
        <h3>{name}</h3>
      </div>
    </div>
  )
}