# Nextjs Convention
## Table of Contents
- Use PascalCase for type names.
- Do not use I as a prefix for interface names.
- Use PascalCase for enum values.
- Use camelCase for function names.
- Use camelCase for property names and local variables.
- Do not use _ as a prefix for private properties.
- Use whole words in names when possible.
- Prefer function declaration over function expression
- Do name React component using UpperCamelCase
- Prefer named export over default export
- Always using Function Declaration over Function Expression
- Order of properties in a React component
- Always use React Fragment to render multiple React components over array and wrapper div:
- Always use <></> over <React.Fragment></React.Fragment> unless you want to pass a key to it
- Always create a function for each reducer actions
- Always use handle prefix for event handler
- Always use on prefix for event handler
- Always use React namespace when accessing React APIs
- Put images to the src/shared/assets/images folder
- Use absolute import to when importing from another module or from the shared module
### Use PascalCase for type names
```typescript
// bad
export type serviceLaneConfigurationPort = {
id: string;
serviceLaneConfigurationId: string;
port: string;
bound?: string;
order: number;
portType: string;
aggregatedPort?: string;
};
// good
export type ServiceLaneConfigurationPort = {
id: string;
serviceLaneConfigurationId: string;
port: string;
bound?: string;
order: number;
portType: string;
aggregatedPort?: string;
};
// bad
export interface IServiceLaneConfigurationPort {
id: string;
serviceLaneConfigurationId: string;
port: string;
bound?: string;
order: number;
portType: string;
aggregatedPort?: string;
};
// good
export interface ServiceLaneConfigurationPort {
id: string;
serviceLaneConfigurationId: string;
port: string;
bound?: string;
order: number;
portType: string;
aggregatedPort?: string;
};
// bad
export enum ServiceLaneConfigurationPortType {
INBOUND = 'inbound',
OUTBOUND = 'outbound',
};
// good
export enum ServiceLaneConfigurationPortType {
Inbound = 'inbound',
Outbound = 'outbound',
};
// bad
export function GetListDischargePort(request: GetListDischargePortRequest): GetListDischargePortResponse {
// ...
}
// good
export function getListDischargePort(request: GetListDischargePortRequest): GetListDischargePortResponse {
// ...
}
// bad
export function getListDischargePort(request: GetListDischargePortRequest): GetListDischargePortResponse {
const ServiceLaneConfigurationPort = request.ServiceLaneConfigurationPort;
// ...
}
// good
export function getListDischargePort(request: GetListDischargePortRequest): GetListDischargePortResponse {
const serviceLaneConfigurationPort = request.serviceLaneConfigurationPort;
// ...
}
// bad
export class ServiceLaneConfigurationPort {
private _id: string;
private _serviceLaneConfigurationId: string;
private _port: string;
private _bound?: string;
private _order: number;
private _portType: string;
private _aggregatedPort?: string;
};
// good
export class ServiceLaneConfigurationPort {
private id: string;
private serviceLaneConfigurationId: string;
private port: string;
private bound?: string;
private order: number;
private portType: string;
private aggregatedPort?: string;
};
// bad
export type ServiceLaneConfigurationPort {
id: string;
slcId: string;
port: string;
bound?: string;
order: number;
portType: string;
aggregatedPort?: string;
};
// good
export type ServiceLaneConfigurationPort {
id: string;
serviceLaneConfigurationId: string;
port: string;
bound?: string;
order: number;
portType: string;
aggregatedPort?: string;
};
Prefer this:
function test() {}
Over this:
const test = () => {}
Because function expression does not have a name and will appear annonymous when debugging with Chrome devtools.
// bad
export function serviceLaneConfigurationPort() {
// ...
}
// good
export function ServiceLaneConfigurationPort() {
// ...
}
Reason: https://basarat.gitbook.io/typescript/main-1/defaultisbad Maintainability concerns here:
// bad
export default function ServiceLaneConfigurationPort() {
// ...
}
// good
export function ServiceLaneConfigurationPort() {
// ...
}
// bad
const test = function() {
// ...
}
// good
function test() {
// ...
}
import * as React from 'react';
type Props = {/* */}
export function Component(props: Props): React.ReactElement {/* */}
// bad
function Component() {
return [
<div key="1">Item 1</div>,
<div key="2">Item 2</div>,
];
}
// good
function Component() {
return (
<>
<div>Item 1</div>
<div>Item 2</div>
</>
);
}
// bad
function Component() {
return (
<React.Fragment>
<div>Item 1</div>
<div>Item 2</div>
</React.Fragment>
);
}
// good
function Component() {
return (
<>
<div>Item 1</div>
<div>Item 2</div>
</>
);
}
export function showTooltipAction(dispatch: React.Dispatch<Action>):
void {
dispatch({ type: 'SHOW_TOOLTIP' });
}
export function hideTooltipAction(dispatch: React.Dispatch<Action>):
void {
dispatch({ type: 'HIDE_TOOLTIP' });
}
function Button(): React.ReactElement {
function handleClick() {/* */}
return <button onClick={handleClick} />;
}
type Props = {
onClick: () => void;
}
The data required to render the page is available at build time ahead of a user’s request.
The data comes from a headless CMS.
The data can be publicly cached (not user-specific).
The page must be pre-rendered (for SEO) and be very fast — getStaticProps generates HTML and JSON files, both of which can be cached by a CDN for performance.
The data needs to be updated on every request and is important for SEO
A Slower TTFB compared to SSG is acceptable
The amount of data that needs to be fetch initially is huge
Form validation: Use react-hook-form and yup.
Animation/Transition: Use framer-motion
Data fetching: axios
Server state management: react-query
Date-timer formatter: luxon