In this post, I will share my coding convention for my React projects.

If you think they are useful for your project especially at the initial setup stage of the project, please feel free to pickup any of them and apply on your team. Although, these rules are rather opinionated based on my personal experience, comments are always welcome.

C1 Quotation.

For string props of HTML element, must use double quote ("). In other places, must use single quote (').

// bad
import * as util from "util"
// good
import * as util from 'util'

// bad
<Foo bar='bar'/>
// good
<Foo bar="bar"/>

// bad
<Foo bar={baz + "baaz"}/>
// good
<Foo bar={`${baz}baaz`}/>

C2 No operator at EOL.

Operator may not be placed at the of line.

// bad
const f = (foo, bar) =>
	foo + bar
// good
const f = (foo, bar) => foo + bar

// bad
const f = (foo, bar) => foo +
	bar
// good
const f = (foo, bar) => foo
	+ bar

// bad
const foo = isBar ?
	baz :
	baaz
// good
const foo = isBar
	? baz
	: baaz

C3 Use dashed css class names.

No camel case, use dash separated name in css.

// bad
.fooBar {
	width: 100%;
}
// good
.foo-bar {
	width: 100%;
}

C4 Class names joining.

All following patterns are acceptable.

<span className="bold no-wrap">lorem ipsum</span>
<span className={'bold no-wrap'}>lorem ipsum</span>
<span className={['bold', 'no-wrap'].join(' ')}>lorem ipsum</span>
<span className={[styles.fooBar].join(' ')}>lorem ipsum</span>
<span className={styles.fooBar}lorem ipsum</span>

However, see the next rule C5.

C5 Conditional class names.

Use .filter(Boolean).join(' ') to join conditional class names.

<span className={[bold && 'bold', styles.fooBar, active && styles.active].filter(Boolean).join(' ')}>lorem ipsum</span>

Use joinClassNames() function when there is a conditional class name.

export const joinClassNames = (...classNames: ReadonlyArray<string | boolean | undefined>) => classNames
	.filter(Boolean)
	.join(' ')

<span className={styles.fooBar}>lorem ipsum</span>
<span className="bold no-wrap">lorem ipsum</span>
<span className={joinClassNames(bold && 'bold', styles.fooBar, active && styles.active)}>lorem ipsum</span>

Note: clsx is a popular library that has a similar purpose with joinClassNames.

C6 No class component.

Use functional component with hooks, no class component except when have to, such as Error Boundary component.

C7 Preserve component display name.

When declaring a component, use module top level function declaration to preserve the component's display name in production build.

Note: this requires babel-plugin-add-react-displayname is enabled in production builds.

// bad
export default () => <div>hello world!</div>
const MyComp = () => <div>hello world!</div>
// good
export default function MyComp() {
	return <div>hello world!</div>
}

// bad
export default forwardRef(function MyComp(props, ref) {
	return <div ref={ref}>hello world!</div>
})
// good
function MyComp(props, ref) {
	return <div ref={ref}>hello world!</div>
 }
 export default forwardRef(MyComp)

Note: because of a bug, the plugin does not set the display name as expected when the component in a forwardRef call returns a Fragment. You can wait for a fix release of the plugin or manually set the display name.

function MyComponent(props, ref) {
	return <><div ref={ref}>hello world!</div></>
}
// fixme when babel-plugin-add-react-displayname releases bug fix, remove this line
MyComponent.displayName = 'MyComponent'
export default forwardRef(MyComponent)

C8 Use tab, no space

Use tab for indentation, no space.

// bad
foo(
  bar
)
// good
foo(
	bar
)

C9 No semicolon

No use of semicolon unless when have to, such as in for loops. In some cases, the explicit separation is required, such as before inline function calls, type casting, ..., use void (more preferred) or semicolon (only when void cannot be used) right before the inline statements which require the explicit separation.

Note: typescript declaration does not require semicolon.

// bad
const baaz = baz + 1;
// good
const baaz = baz + 1

// bad
const baaz = baz;
['ab', 'b'].forEach(console.log)
// good
const baaz = baz
void ['ab', 'b'].forEach(console.log)

// bad
const baaz = baz;
(async () => {
	// some async calls
})()
// good
const baaz = baz
void (async () => {
	// some async calls
})()

// good
for (let i = 0; i < str.length; i++) {
	// process character
}

// bad
let foo: {bar: number; baaz: string}
// good
let foo: {
	bar: number
	baaz: string
}

// bad
import {promisify} from 'util';
// good
import {promisify} from 'util'

// bad
import {promisify} from 'util'

;(async () => {await mongoose.connect()})()
// good
import {promisify} from 'util'

void (async () => {await mongoose.connect()})()

C10 Multiple lines props.

When component props are declared in multiple lines, each props must start a new line.

When component props are declared in multiple lines, tag close must be placed in a separated line.

// bad
<Comp foo="bar" className={joinClassNames(
	styles.foo,
	active && 'active'
)}
/>
// good
<Comp foo="bar" className={joinClassNames(
	styles.foo,
	active && 'active'
)}/>

// bad
<Comp foo="bar"
	bar="baaz"/>
<Comp foo="bar"
	bar="baaz"
	/>
// good
<Comp foo="bar" bar="baaz"/>
<Comp
	foo="bar"
	bar="baaz"
	/>

// bad
<Comp foo="bar" baz={calc(
	baaz,
	true
)} baaz="baar"
>hello!</Comp>
// good
<Comp foo="bar" baz={calc(
	baaz,
	true
)} baaz="baar">hello!</Comp>
<Comp
	foo="bar"
	baz={calc(
		baaz,
		true
	)}
	baaz="baar"
>hello!</Comp>

C11 Trailing comma

Trailing commas are acceptable, and optional.

Any of followings are acceptable.

func(a, b, c)
func(a, b, c,)
func(
	a,
	b,
	c,
)
func(
	a,
	b,
	c
)
const a = {
	b: 'c',
	d: 'e'
}
const a = {
	b: 'c',
	d: 'e',
}
const a = [1, 2]
const a = [1, 2,]
const a = [
	1,
	2
]
const a = [
	1,
	2,
]

C12 Nested ternary operator.

Nested ternary operators are recommended with appropriate indentations.

return isFoo
	? bar
	: isBaz
		? baaz
		: baaz

C13 Parenthesis.

Eliminate parenthesis when no need to use. Refer to Operator Precedence to acknowledge the execution strategy.

// bad
(foo && (bar || baz)) || (foo && bar)
// good
foo && (bar || baz) || foo && bar

// bad
return (
	<div>
		lorem ipsum
	</div>
)
// good
return <div>
	lorem ipsum
</div>

C14 Curly braces.

Curly braces can be omitted when not required.

Any of the followings are acceptable.

// not recommeneded
if (isVerbose)
	console.log('hello, world')
// recommeneded
if (isVerbose) console.log('hello, world')
if (isVerbose) {
	console.log('hello, world')
}
if (isVerbose) console
	.log('hello, world')
if (isVerbose) console.log(
	'hello, world'
)
if (
	isVerbose
) console.log(
	'hello, world'
)

// recommended
const a = () => b
const a = () => {
	return b
}

// not recommeneded
for (let i = 0; i < 10; i++)
	console.log(i)
// recommended
for (let i = 0; i < 10; i++) console.log(i)
for (
	let i = 0;
	i < 10;
	i++
) console.log(i)

C15 Literal template.

Must use literal template for string concatenation. Recommend using literal template when there is a new line.

// bad
const bar = 'hello, ' + bar
// good
const bar = `hello, ${bar}`

// not recommended
const bar = 'hello\nworld'
// recommended
const bar =`hello
wolrd`

C16 Style import.

When import an css style, use default import.

// bad
import * as styles from './Comp.scss'
import {active, lastChild} from './Comp.scss'

// good
import styles from './Comp.scss' 

C17 Strict comparison.

Use strict comparison, no loose comparison.

// bad
a == b
// good
a === b

C18 Closing braces' indentation.

If open brace ((, {) is at an end of a line, the opposite closing brace (), }) must starts a new line.

// bad
if (x
) {}
if (
	x) {}
// good
if (
	x
) {}
if (x) {}

// bad
if (x) {
	console.log('hi')}
// good
if (x) {console.log('hi')}
if (x) {
	console.log('hi')
}