Skip to content

Instantly share code, notes, and snippets.

@lucasKoyama
Last active August 11, 2025 20:38
Show Gist options
  • Save lucasKoyama/7545e28e857a8045976ab9988d0b0353 to your computer and use it in GitHub Desktop.
Save lucasKoyama/7545e28e857a8045976ab9988d0b0353 to your computer and use it in GitHub Desktop.
Next.js 14 CheatSheet

Next.js 14 Cheat Sheet

Almost all titles have links to the oficial NextJS documentation!

Summary

CLI to create

npx create-next-app@latest --typescript

Going to folder

cd <project-name>

Installing modules

npm install

Running dev. server

npm run dev

NextJS folders Structure

  • /app: Contains all the routes, components, and logic for your application, this is where you'll be mostly working from.
  • /app/lib: Contains functions used in your application, such as reusable utility functions and data fetching functions.
  • /app/ui: Contains all the UI components for your application, such as cards, tables, and forms. To save time, we've pre-styled these components for you.
  • /app/ui/fonts.ts is where you should keep external fonts, by doing that, Next will download them previously to reduce fetching.
  • /public: Contains all the static assets for your application, such as images.
  • /scripts: Contains a seeding script that you'll use to populate your database in a later chapter. Config Files: You'll also notice config files such as next.config.js at the root of your application. Most of these files are created and pre-configured when you start a new project using create-next-app. You will not need to modify them in this course.

global.css at root layout.tsx home.module.css module CSS allow you to scope CSS to a component by automatically creating unique class names, so you don't have to worry about style collisions as well.

Use the <Image/> component with the width and height equal to the aspect ratio of the image

image image

Each `folder` is a route, inside the folder you need a `page.tsx` which will be the "`index.html`", the `layout.tsx` will be some standard layouts for the page.

<Link /> component works just like the tag <a>, but with the difference that it prefetches other pages, resulting in a faster navigation between pages due to the background preload

Use async in the function Page() and then use an await to fetch some data directly with SQL queries or ORM.

image Use Promise.all([Promise1, Promise2, Promise3]) for parallel data fetching

Rendering Static vs Dynamic

  • What is? Fetches data > render the page > cache it to deliver later on requests > Content Delivery Network (CDN) > clients
  • When should be used? When using UI with no data or data that is shared across users
  • Pros: Faster Websites, Reduced Server Load, SEO.
  • What is? Content is rendered on the server for each user at request time (when the user visits the page)
  • Pros: Real-Time Data, User-Specific Content (personalized content based on user interactions), Request Time Information. With dynamic rendering, your application is only as fast as your slowest data fetch.

import { unstable_noStore as noStore } from 'next/cache'; Use the noStore() inside the server components or inside the async functions

  • loading.tsx is a special Next.js file, it show as a replacement while page content loads.
  • Inside it can be rendered static content like "loading skeletons"
  • To avoid the loading.tsx rendering in other sub-routes, move the page.tsx and loading.tsx to a folder with ( ) image image Contents like CardWrapper, RevenueChart and LatestInvoices are the components with data being fetched, while the data is streaming the Suspense loads the skeletons which are just the prototype structures of the UI
  1. Capture the user's input. With handleChange
  2. Update the URL with the search params.
  3. Keep the URL in sync with the input field.
  4. Update the table to reflect the search query.

Searching component + sync with field and URL + debouncing optimization

'use client';

import { MagnifyingGlassIcon } from '@heroicons/react/24/outline';
import { usePathname, useSearchParams, useRouter } from 'next/navigation';
import { useDebouncedCallback } from 'use-debounce';

export default function Search({ placeholder }: { placeholder: string }) {
  const searchParams = useSearchParams();
  const pathname = usePathname();
  const { replace } = useRouter();

  const handleSearch = useDebouncedCallback((term) => {
    console.log(`Searching... ${term}`);
    const params = new URLSearchParams(searchParams);
    // params.set('page', '1'); pagination
    if (term) {
      params.set('query', term);
    } else {
      params.delete('query');
    }
    replace(`${pathname}?${params.toString()}`);
  }, 500);

  return (
    <div className="relative flex flex-1 flex-shrink-0">
      <label htmlFor="search" className="sr-only">
        Search
      </label>
      <input
        className="peer block w-full rounded-md border border-gray-200 py-[9px] pl-10 text-sm outline-2 placeholder:text-gray-500"
        placeholder={placeholder}
        onChange={(event) => handleSearch(event.target.value)}
        defaultValue={searchParams.get('query')?.toString()}
      />
      <MagnifyingGlassIcon className="absolute left-3 top-1/2 h-[18px] w-[18px] -translate-y-1/2 text-gray-500 peer-focus:text-gray-900" />
    </div>
  );
}

Allows fresh data to be fetched from the server.

  • error.tsx UI rendered upon any error (catch-all)
  • not-found.tsx UI served when used the { notFound } from 'next/navigation';

Checklist to optimize pages

  • Created a database in the same region as your application code to reduce latency between your server and database.
  • Fetched data on the server with React Server Components. This allows you to keep expensive data fetches and logic on the server, reduces the client-side JavaScript bundle, and prevents your database secrets from being exposed to the client.
  • Used SQL to only fetch the data you needed, reducing the amount of data transferred for each request and the amount of JavaScript needed to transform the data in-memory.
  • Parallelize data fetching with JavaScript - where it made sense to do so.
  • Implemented Streaming to prevent slow data requests from blocking your whole page, and to allow the user to start interacting with the UI without waiting for everything to load.
  • Move data fetching down to the components that need it, thus isolating which parts of your routes should be dynamic in preparation for Partial Prerendering.
  • Using Debouncing to prevent triggers in requests to the server everytime something occurs, adding a delay before the next thing, like for each keystroke make a request to query something, adding a debounce, it adds a delay before triggering the query, allowing multiples keystrokes without triggering requests

Extra

  • Project in GitHub
  • Deploy in Vercel
  • In Vercel dashboard > "Storage" > Postgres > After creation > tab .env.local > "Show Secrets" > "Copy Snippet"
  • In your project file .env paste the secrets copied from the DB
  • run in project folder npm i @vercel/postgres
  • For "automatic" database seeding checkout seed.js file, and insert the the script in package.json > "seed": "node -r dotenv/config ./scripts/seed.js" then run npm run seed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment