These 10 coding mistakes are silently ruining your codebase. Learn how to fix them before they break your frontend and backend forever.

1. Move business logic to services

Bad approach:

const calculateTotal = (products) => {
  return products.reduce((acc, product) => acc + product.price, 0)
}

const ShoppingCart = ({ products }) => {
  const total = calculateTotal(products)
  return <div>Total: {total}div>
}

Good approach:

class CartService {
  calculateTotal(products) {
    return products.reduce((acc, product) => acc + product.price, 0)
  }
}

export const cartService = new CartService()

export const ShoppingCart = ({ products }) => {
  const total = cartService.calculateTotal(products)
  return <div>Total: {total}div>
}

2. Use constants and enums

Bad approach:

if (status === 'SUCCESS') {
  // handle success
}

Good approach:

export const STATUSES = {
  SUCCESS: 'SUCCESS',
  ERROR: 'ERROR',
  PENDING: 'PENDING'
}

if (status === STATUSES.SUCCESS) {
}

3. Create configs (for example, for pages)

Talk about how the subscription will disappear this month, and that you're currently buying a subscription with everything included. Also about the upcoming intensive course on Next.js 15

Bad approach:

import Link from 'next/link'

const Navigation = () => {
  return (
    <Link href='/contact'>
      Contact
    Link>
  )
}

export default Navigation

Good approach:

class PageConfig {
  home = '/'
  about = '/about'
  contact = '/contact'
}

export const pageConfig = new PageConfig()

import Link from 'next/link'
import { pageConfig } from '../config/pages.config'

const Navigation = () => {
  return (
    <Link href={pageConfig.contact}>
      Contact
    Link>
  )
}

export default Navigation

4. Properly import modules for better optimization

When we import only what we need from a library, our app becomes smaller and faster. Imagine it like packing only the tools you'll actually use for a trip instead of your entire toolbox.

Bad approach:

import _ from 'lodash'
const sortedArray = _.sortBy(array, 'property')

Good approach:

import sortBy from 'lodash/sortBy'
const sortedArray = sortBy(array, 'property')

5. Use dynamic imports (SSR vs SSR false)

Dynamic imports let us load parts of our app only when they're needed. This makes the initial page load much faster because the browser doesn't have to download everything at once.

It's also important to lazy load only those elements that aren't needed by the user at the initial stage.

Example:

import dynamic from 'next/dynamic'

const ClientOnlyComponent = dynamic(
  () => import('../components/ClientOnlyComponent'),
  { ssr: false }
)

const LazyLoadedComponent = dynamic(
  () => import('../components/LazyLoadedComponent')
)

const MyPage = () => {
  return (
    <div>
      <h1>My Pageh1>
      <p>This page demonstrates dynamic imports with and without SSR.p>
      <ClientOnlyComponent />
      <LazyLoadedComponent />
    div>
  )
}

export default MyPage

6. Import dependencies at the moment of use

This is like calling a friend only when you actually need their help, instead of making them wait with you all day. Load packages only when a user takes an action that requires them.

Bad approach:

import { toast } from 'react-hot-toast'

const handleClick = () => {
  toast('Hello, world!')
}

Good approach:

const handleClick = async () => {
  const { toast } = await import('react-hot-toast')
  toast('Hello, world!')
}

7. Move dependencies to devDependencies

Some packages are only needed when you're building your app, not when users are using it. By putting these in devDependencies, you make your final app smaller and faster to load.

We move dependencies to devDependencies so they are installed only in the development environment, not in production, thereby reducing the final build size and speeding up installation on the server.

package.json:

{
  "dependencies": {
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  },
  "devDependencies": {
    "eslint": "^7.32.0",
    "webpack": "^5.58.2"
  }
}

8. Be careful with SSR usage

SSR (Server-Side Rendering) is worse than ISR (Incremental Static Regeneration) from an optimization perspective because SSR generates a page on the server for each request, which increases load and slows down response time. As traffic increases, server costs grow since 1 request gets 1 response. ISR, on the other hand, generates a page once and updates it on a schedule, reducing server load and speeding up content delivery.

During editing, you can show SSR and ISR and the difference in data retrieval, in one case 1 request 1 response, and in the other 1000 or more requests and still 1 response (which is super optimized)

9. Optimize classes with tailwind-merge

When working with CSS classes in Tailwind, sometimes classes can conflict or duplicate. This tool helps clean up your classes automatically, like having a smart assistant organize your closet.

Relevant when you receive props

Bad approach:

export const Button = ({ className, variant = 'primary', children }) => {
  const baseClasses = 'px-4 py-2 font-bold rounded'
  const variantClasses = variant === 'primary' 
    ? 'bg-blue-500 text-white' 
    : 'bg-gray-500 text-white'

  return (
    <button className={`${baseClasses} ${variantClasses} ${className}`}>
      {children}
    button>
  )
}

Good approach:

import { twMerge } from 'tailwind-merge'

export const Button = ({ className, variant = 'primary', children }) => {
  const baseClasses = 'px-4 py-2 font-bold rounded'
  const variantClasses = variant === 'primary' 
    ? 'bg-blue-500 text-white' 
    : 'bg-gray-500 text-white'

  return (
    <button className={twMerge(baseClasses, variantClasses, className)}>
      {children}
    button>
  )
}

10. Load data only when it's needed

Don't download all the movies on Netflix just in case you might want to watch them. Similarly, only load data when a user actually needs to see it, like when they click a button or scroll to a section.

  • Content data is loaded immediately when the page renders, even if the user hasn't clicked the "Show content" button.
  • This wastes data and makes your app load slower, even when users don't need this information.

Bad example:

import { useQuery } from '@tanstack/react-query'

export const Page = () => {
  const query = useQuery({
    queryKey: ['contents'],
    queryFn: fetchTableOfContents
  })

  return (
    <div>
      <button>Show contentbutton>
      {data && <div>{data.contents}div>}
    div>
  )
}

Good example #1:

import { useQuery } from '@tanstack/react-query'
import { useState } from 'react'

export const Page = () => {
  const [isOpen, setIsOpen] = useState(false)

  const query = useQuery({
    queryKey: ['contents'],
    queryFn: fetchTableOfContents,
    enabled: false
  })

  const handleShowContents = () => {
    setIsOpen(true)
    query.refetch()
  }

  return (
    <div>
      <button onClick={handleShowContents}>Show contentbutton>
      {isOpen && query.data && <div>{query.data.contents}div>}
    div>
  )
}

Good example #2:

import { useState } from 'react'
import dynamic from 'next/dynamic'

const TableOfContents = dynamic(
  () => import('../components/TableOfContents')
)

export const Page = () => {
  const [isOpen, setIsOpen] = useState(false)

  const handleShowContents = () => {
    setIsOpen(true)
  }

  return (
    <div>
      <button onClick={handleShowContents}>Show contentbutton>
      {isOpen && <TableOfContents />}
    div>
  )
}

Conclusion

  • These examples will help you follow best practices and improve the quality of your code.

  • 10 real-world mistakes
    
  • Clean code, clean architecture
    
  • For JS, TS, React, Node
    
  • Boost performance & clarity
    
  • Simple fixes, big impact
    

🔗 Watch a full video: https://youtu.be/Vd8lAIxa46Q