Yevhen Klymentiev
dark
light
console
darkness
y.klymentiev@gmail.com
Coding Standards|Rules, conventions, and best practices I follow and recommend to teams

React / JSX

Write clean, readable, and maintainable React code by following consistent JSX and component patterns.

These rules help ensure clarity in UI logic, improve component reusability, reduce unnecessary complexity and re-renders, and promote accessibility, testability, and long-term scalability in React applications.

One component per file

Each file should contain only one top-level component. This makes components easier to locate, reason about, test, and reuse. It also improves auto-imports and keeps file responsibilities clear.

Tip: If small helper components are tightly coupled and not reused, they may be defined inline but not exported.

JSX
Copied!
1// Card.tsx
2export function Card() { ... }
3export function CardHeader() { ... }
4export function CardBody() { ... }
JSX
Copied!
1// Card.tsx
2export function Card() { ... }
3
4// CardHeader.tsx
5export function CardHeader() { ... }

Avoid prop drilling

Passing props through many intermediate components (prop drilling) creates brittle code and unnecessary coupling. Prefer React Context, composition, or state management solutions for deeply shared state.

JSX
Copied!
1// App → Layout → Page → Sidebar → Profile → UserAvatar
2<Layout user={user} />
3
4function Sidebar({ user }) {
5  return <Profile user={user} />;
6}
JSX
Copied!
1<UserProvider value={user}>
2  <Layout />
3</UserProvider>
4
5// Inside deeply nested component
6const user = useUser();

Keep components small and focused

A component should do one thing well. Split large, complex components into smaller, reusable ones to improve clarity and testability.

JSX
Copied!
1function Dashboard() {
2  return (
3    <div>
4      <Nav />
5      <Sidebar />
6      <Content />
7      <Footer />
8      <Modal />
9      <Toast />
10    </div>
11  );
12}
JSX
Copied!
1function Dashboard() {
2  return (
3    <>
4      <Layout />
5      <OverlayElements />
6    </>
7  );
8}

Destructure props at the top level

Destructuring props improves readability and makes dependencies explicit. It also reduces repetitive access like props.name.

JSX
Copied!
1function Profile(props) {
2  return <h1>{props.name}</h1>;
3}
JSX
Copied!
1function Profile({ name }) {
2  return <h1>{name}</h1>;
3}

Use explicit return in multi-line JSX

When returning multi-line JSX, always wrap the content in parentheses to improve readability and avoid automatic semicolon insertion issues.

JSX
Copied!
1return
2  <div>
3    Hello
4  </div>;
JSX
Copied!
1return (
2  <div>
3    Hello
4  </div>
5);

Use fragments instead of unnecessary divs

Avoid creating extra DOM nodes. Use React Fragments (<>...</>) to group elements without affecting the DOM structure.

JSX
Copied!
1return (
2  <div>
3    <Title />
4    <Text />
5  </div>
6);
JSX
Copied!
1return (
2  <>
3    <Title />
4    <Text />
5  </>
6);

Or full Fragments syntax:

JSX
Copied!
1cards.map(curr => (
2  <Fragment key={curr.id}>
3    <Title>{curr.title}</Title>
4    <Description text={curr.text} />
5  </Fragment>
6));

Always use 'key' in list rendering

When rendering lists, always specify a key prop with a unique and stable value to help React track and optimize DOM updates.

JSX
Copied!
items.map((item) => <li key={item.id}>{item.label}</li>);

Use conditional rendering properly

Apply conditional rendering clearly and consistently. Avoid deeply nested ternaries and chained logic. Prefer short-circuiting (&&), early returns, or abstraction via components when rendering based on state.

JSX
Copied!
return user ? (user.isAdmin ? <AdminPanel /> : <UserPanel />) : <LoginPrompt />

Early return:

JSX
Copied!
1if (!user) return <LoginPrompt />;
2if (user.isAdmin) return <AdminPanel />;
3return <UserPanel />;

Short-circuit:

JSX
Copied!
{isModalOpen && <Modal />}

Abstraction:

JSX
Copied!
1function AuthGate({ user, children }) {
2  if (!user) return <LoginPrompt />;
3  return children;
4}
5
6// Usage
7<AuthGate user={user}>
8  <Dashboard />
9</AuthGate>

Keep logic out of JSX

Move as much logic as possible out of the return block to avoid cluttering your JSX. JSX should ideally contain just simple interpolation and conditional rendering. This improves readability and separation of concerns.

JSX
Copied!
1return (
2  <>
3    ...
4    {items.filter(i => i.active && i.count > 0).map(i =>
5      i.type === "premium" ? <PremiumItem key={i.id} {...i} /> : <Item key={i.id} {...i} />
6    )}
7    ...
8  </>
9);
JSX
Copied!
1
2const renderItems = useCallback(
3  () => {
4    return (
5      items
6        .filter(i => i.active && i.count > 0)
7        .map(i => i.type === "premium"
8          ? <PremiumItem key={i.id} {...i} />
9          : <Item key={i.id} {...i} />
10        );
11    );
12  }, [items]
13}
14
15return (
16  <>
17    ...
18    {renderItems(items)};
19    ...
20  </>
21);

Use 'useMemo' and 'useCallback' wisely

Use useMemo and useCallback to memoize expensive calculations or stable functions — but only when needed. Overusing them can hurt performance.

JSX
Copied!
const memoizedValue = useMemo(() => 2 + 2, []);
JSX
Copied!
const filteredList = useMemo(() => list.filter(fn), [list]);

Keep side effects inside 'useEffect'

Any code that interacts with the outside world (e.g., API calls, timers, DOM) should be placed inside useEffect, not directly in the render flow.

JSX
Copied!
1useEffect(() => {
2  fetch('/api/data');
3}, []);

Avoid Unnecessary State

Only store in state what cannot be derived from props or other state. Derived values should be computed in-place or with useMemo

JSX
Copied!
const [fullName, setFullName] = useState(`${first} ${last}`);
JSX
Copied!
const fullName = `${first} ${last}`;

Define event handlers outside JSX

Declare event handlers as named functions outside JSX to improve readability, enable reuse, and avoid unnecessary re-renders due to anonymous function re-creation.

JSX
Copied!
<button onClick={() => doSomething(id)}>Click</button>
JSX
Copied!
1function handleClick() {
2  doSomething(id);
3}
4
5return <button onClick={handleClick}>Click</button>;

Always follow hook rules

Hooks must be called unconditionally and in the same order on every render. Never place hooks inside conditions, loops, or nested functions.

JSX
Copied!
1if (isAdmin) {
2  useEffect(() => {
3    // Invalid!
4  }, []);
5}
JSX
Copied!
1useEffect(() => {
2  if (isAdmin) {
3    // Valid
4  }
5}, [isAdmin]);

Use controlled components for forms

Prefer controlled inputs (tied to component state) over uncontrolled ones. This makes validation, resets, and dynamic behaviors easier to manage and debug.

JSX
Copied!
<input defaultValue="John" />
JSX
Copied!
1const [name, setName] = useState("John");
2
3function handleNameChange(e: React.ChangeEvent<HTMLInputElement>) {
4  setName(e.target.value);
5}
6
7return <input value={name} onChange={handleNameChange} />;

Memoize expensive components and values

Use React.memo, useMemo, and useCallback to avoid unnecessary recalculations or re-renders — especially for heavy computations or frequently re-rendering child components.

JSX
Copied!
1const memoizedItems = useMemo(() => computeItems(items), [items]);
2<ExpensiveList items={memoizedItems} />

Or for components:

JSX
Copied!
const ExpensiveList = React.memo(({ items }) => { ... });

Split components by responsibility

If a component handles multiple concerns (data fetching, rendering UI, handling interactions), split responsibilities into separate components or hooks. This promotes clarity and reusability.

JSX
Copied!
1function Dashboard() {
2  const [data, setData] = useState(null);
3  useEffect(() => { fetchData().then(setData); }, []);
4
5  return (
6    <div>
7      {data ? <List data={data} /> : <Spinner />}
8    </div>
9  );
10}
JSX
Copied!
1function Dashboard() {
2  const data = useDashboardData();
3  return <DashboardContent data={data} />;
4}

Use lazy loading for heavy components

Use React.lazy with Suspense to defer loading large components until needed. This improves initial load performance, especially for route-based or modal content.

JSX
Copied!
1const BigChart = React.lazy(() => import('./BigChart'));
2
3<Suspense fallback={<Spinner />}>
4  <BigChart />
5</Suspense>

Use error boundaries for critical UI

Wrap risky parts of your UI (routes, dynamic widgets, third-party embeds) in error boundaries to prevent a full app crash and show fallback UI.

JSX
Copied!
1//Note: Error boundaries must be class components or libraries like react-error-boundary
2<ErrorBoundary fallback={<FallbackUI />}>
3  <RiskyWidget />
4</ErrorBoundary>

Extract logic into reusable hooks

Encapsulate shared logic (e.g., data fetching, timers, form state) in custom hooks instead of repeating code in multiple components.

JSX
Copied!
1function ComponentA() {
2  const [value, setValue] = useState(0);
3  useEffect(() => {
4    const timer = setInterval(() => setValue(v => v + 1), 1000);
5    return () => clearInterval(timer);
6  }, []);
7}
JSX
Copied!
1function useTick(interval = 1000) {
2  const [tick, setTick] = useState(0);
3  useEffect(() => {
4    const t = setInterval(() => setTick(v => v + 1), interval);
5    return () => clearInterval(t);
6  }, [interval]);
7  return tick;
8}

Make components testable

Write components in a way that makes them easy to test: avoid tightly coupled logic, use dependency injection when needed, and ensure selectors (e.g., data-testid) are available for queries.

JSX
Copied!
<button onClick={() => doSomethingSecret()}>Go</button>
JSX
Copied!
<button data-testid="submit-button" onClick={handleSubmit}>Go</button>

Use shorthand for boolean props

When a boolean prop is set to true, use the JSX shorthand syntax for cleaner and more idiomatic code.

JSX
Copied!
<Navbar showTitle={true} />
JSX
Copied!
<Navbar showTitle />

Don’t wrap string props in braces

For static string values, omit to reduce visual clutter and improve consistency.

JSX
Copied!
<Sidebar title={"My Special App"} />
JSX
Copied!
<Sidebar title="My Special App" />

Avoid conflicting prop names

Avoid using prop names that conflict with reserved DOM attributes like className, style, or id unless you intend to pass them directly to a DOM element.

JSX
Copied!
1<MyComponent className="dark" />
2<MyComponent style="compact" />
JSX
Copied!
1<MyComponent variant="dark" />
2<MyComponent layout="compact" />

Use consistent quotes in JSX

Use double quotes for JSX attributes (HTML or custom components), and use single quotes for strings inside JavaScript/TypeScript code. This avoids escaping and improves visual consistency.

JSX
Copied!
1<div className='header'>
2  {title === "Main" ? 'Yes' : "No"}
3</div>
JSX
Copied!
1<div className="header">
2  {title === 'Main' ? 'Yes' : 'No'}
3</div>

Use self-closing tags when possible

For elements that don’t have children, always use self-closing tags. It makes code cleaner and less error-prone.

JSX
Copied!
1<Input></Input>
2<div className="spacer"></div>
JSX
Copied!
1<Input />
2<div className="spacer" />

Order JSX props for readability

When a component has many props, order them consistently to improve scanability and reduce cognitive load. While there's no single "correct" order, a common convention is:

  • Essential props (e.g. id, to, href, type)

  • Structural/config props (e.g. variant, size, align, direction)

  • Booleans and modifiers (disabled, loading, dense)

  • Event handlers (onClick, onChange, onSubmit)

  • Styling props (className, style)

  • Data & children (title, label, value, children)

JSX
Copied!
1<Button
2  onClick={handleClick}
3  size="lg"
4  className="cta"
5  variant="primary"
6  disabled
7  id="main-action"
8>
9  Submit
10</Button>
JSX
Copied!
1<Button
2  id="main-action"
3  variant="primary"
4  size="lg"
5  disabled
6  onClick={handleClick}
7  className="cta"
8>
9  Submit
10</Button>
Styleguide: React / JSX | Yevhen Klymentiev