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.
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
- 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 ...
- check size of imports (i.e. with VSCode plugin import costs (opens in a new tab)) and reduce where possible (green is fine, red should be reviewed)
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?
Here we apply the tightly-coupled architecture (opens in a new tab).
- 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>
)
}