New component
Location and structure
The files and folder structure follows the
tightly-coupled architecture and
underlies the Next.js file-system router. 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?
- 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) and reduce where possible (green is fine, red should be reviewed)
Why?
- use function declaration syntax instead of arrow function syntax.
// ❌
const NewComponent = (...) => { ... }
// ✅
function NewComponent(...) { ... }
Why?
- 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?
- 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?
- 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?
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?
- provide feedback for every write, delete, update operation
Why?
- use proper code formatting via Prettier & linting via ESLint
Why?
- check console (browser and terminal) for any errors or warnings
Why?
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>
)
}