Setup using Vite

Vite is a build tool and dev server for React app.

  • A front-end tool to quickly develop app/prototype, SPAs, Dashboard, interal tooling
  • Minimal and any extra tech will need to be added: routing, SSR, testing setup, API routes

Basic Commands

  • Creating the Project : npm create vite@latest my-app
    • Select React framework, and Javascript or Typescript variant
  • Running the Developer Server : npm run dev in the project root folder
  • Building the Project : npm run build in the project root folder

Folder Structure

my-vite-app/
├── index.html
├── src/
│   ├── App.jsx         # root component that wraps all other components
│   ├── main.jsx        # entry point of website
│   ├── pages/
│   │   ├── Home.jsx    # Home page (/)
│   │   └── About.jsx   # About page (/about)
│   ├── store.js
└── public/             # holds static assets
    ├── images/         # accessed from root (/images/*)
    ├── sitemap.xml     # good for SEO (/sitemap.xml)
    └── robots.txt      # accessed from root (/robots.txt)
├── package.json
└── vite.config.js

Minimal Project + React Router

React Router is used to add routing functionality into the Vite app

  • the main.jsx is the same as index.js in other React app (except Next.js)
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App.jsx';

ReactDOM.createRoot(document.getElementById('root')).render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

// App.jsx
import { Routes, Route, Link } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';

export default function App() {
  return (
    <div>
      <nav>
        <Link to='/'>Home</Link> |{" "}
        <Link to='/about'>About</Link>
      </nav>
      <Routes>     # Routes is the main container that swaps components out depending on the current URL
        <Route path='/' element={<Home />} />
        <Route path='/about' element={<About />} />
      </Routes>
    </div>
  );
}

// pages/Home.jsx
export default function Home() {
  return (
    <div>
      <h1>Home Page</h1>
    </div>
  );
}

// pages/About.jsx
export default function About() {
  return <h1>About Page</h1>;
}

Setup using Next.js

Next.js is a framework for full-stack React app.

  • It has built-in features like routing, SSR, API routes, and testing setup (Jest)
  • Good for SEO and a variety of apps like blogs, marketing sites, e-commerce, enterprise apps, SaaS and large apps

Basic Commands

  • Creating the Project : npx create-next-app@latest my-app
    • ESLint : Tool to make sure Javascript and Typescript codes are clean and error-free
    • Turbopack: Build tool to package code efficiently for dev and production
  • Running the Developer Server : npm run dev in the project root folder
  • Building the Project : npm run build in the project root folder
    • Testing the build : npm run start to test the production build

Folder Structure

my-next-app/
├── app/                 # the root for all navigation and links
│   ├── layout.jsx       # Optional Global Layout - entry point if file exist
│   ├── page.jsx         # Home page (/) - normal entry point if layout.jsx did not exist
│   ├── about/
│   │   └── page.jsx     # About page (/about)
└── public/              # holds static assets
    ├── images/          # accessed from root (/images/*)
    ├── sitemap.xml     # good for SEO (/sitemap.xml)
    └── robots.txt       # accessed from root (/robots.txt)
├── store.js
├── next.config.js
├── tsconfig.json
└── package.json
  • In Next.js App Router (Next 13+), your entire site structure and navigation are based on the folder and file structure inside app/ folder
    • The app/ and public/ folders must stay the same.
    • Special files like layout.js and page.js can not be renamed and need to be in their expected locations for proper navigation
    • The subfolders of app/ can be organized however you want
    • Config files like next.config.js, package.json, tsconfig.json must be in the root
    • Standard Node.js rules apply. Keep node_modules/ in root; you don’t move this around.
    • You can create folders like components/, hooks/, utils/, styles/, lib/, etc. anywhere as long as they're imported correctly

Minimal Project with Global Layout

Next.js already has built in routing and SSR.

  • app/layout.jsx acts as the entry point and root component for the app so it's similar to combining main.jsx + App.jsx in Vite
export const metadata = {
  title: 'My Next.js App',
  description: 'Minimal Next.js example with SSR and layout',
};

export default function RootLayout({ children }) {
  return (
    <html lang='en'>
      <body>
        <header>
          <nav>
            <a href='/'>Home</a>
            <a href='/about'>About</a>
          </nav>
        </header>
        <main>{children}</main> #{children} is where the components will be swapped
        <footer></footer>
      </body>
    </html>
  );
}

// app/page.jsx (Home Page)
export const dynamic = 'force-dynamic'; // Ensures SSR every request

export default async function HomePage() {
  // Fetch some server-side data
  const res = await fetch('https://jsonplaceholder.typicode.com/posts/1', {
    cache: 'no-store', // Forces SSR fetch
  });
  const data = await res.json();

  return (
    <section>
      <h1>Welcome to My Next.js App!</h1>
      <h2>SSR Example:</h2>
      <p><strong>Title:</strong> {data.title}</p>
      <p><strong>Body:</strong> {data.body}</p>
    </section>
  );
}

// app/about/page.jsx (About Page)
export default function AboutPage() {
  return <h1>About Page</h1>;
}

// next.config.js (optional but recommended)
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
};

module.exports = nextConfig;

Key Files

  • package.json : includes the scripts and dependencies of the project
    • homepage : the URL for the homepage of the site
    • scripts : the scripts to manage project
      • Sample: "script": { "scriptName": "cmd code", }
      • "scripts": {
            "dev": "next dev --turbopack",
            "build": "next build --turbopack",
            "analyze": "cross-env ANALYZE=true next build",
            "start": "next start",
            "lint": "eslint"
          },
      • dev : script used when starting development/testing server
      • build : script for creating a production-ready build
      • analyze : script to anaylyze build bundle
  • *.config.js : configuration file for the tool you're using (Vite or Next)
  • next.config.mjs : Next.js project configuration file to control development/production build by adding key-value pairs to the nextConfig object
    • basePath : to adjust the URL of of navigation links; used when deploying app under a sub-path of a domain
    • assetsPrefix : to adjust the URL of the Javascript and CSS files that loads from _next/ folder like _next/static/
    • assetsPrefix does NOT affect files in the public/ folder
    • Reference: nextjs.org/docs/app/api-reference/config/next-config-js
  • app/not-found.js : the page that is shown for 404 error
    • Use notFound() from import { notFound } from "next/navigation"; to throw a 404 error for testing

Dynamic Vs Static Sites

  • Dynamic Site Features
    • Server Side Rendering (SSR)
    • API Routes
    • Dynamic Routing
    • Authenticatoion/Sessions : can use cookies, JWTs, or OAuth flows
    • Use cases: E-commerce, dashboards, social media feeds, requires authentication
  • Static Site Features
    • Pre-rendered HTML : fast rendering
    • Works on Static Host
    • Dynamic Routing
    • Use cases: Blogs, docs, marketing site, portfolio, gallery

Optimizing Production Build

Use Next.js bundle analyzer

  1. Install the analyzer: npm install @next/bundle-analyzer --save-dev and npm install cross-env --save-dev in the project root
  2. Update package.json to have the following key-value pair in "scripts"
  3. "scripts": {
      ...
      "analyze": "cross-env ANALYZE=true next build",
    }
  4. Configure next.config.js and set it as
  5. import bundleAnalyzer from '@next/bundle-analyzer';
    
    const withBundleAnalyzer = bundleAnalyzer({
      enabled: process.env.ANALYZE === 'true',
    });
    
    /** @type {import('next').NextConfig} */
    const nextConfig = {
      reactStrictMode: true,
      // swcMinify: true, // Next.js 14+ always minifies JS in production
      compiler: {
        // Remove console logs only in production
        removeConsole: process.env.NODE_ENV === "production"
    };
    
    export default withBundleAnalyzer(nextConfig);
  6. Run the analyzer: npm run analyze for windows, ANALYZE=true npm run build for Mac/Linux npx cross-env ANALYZE=true npm run buildin the project root

Tips

  • Tree-shake libraries : use named imports to only import components you use
    • import oneFunction from 'lodash/manyFunctions';
  • Dynamic imports : use dynamic imports for pages/components that are rarely used
    • Instead of import MasonryComponent from './MasonryComponent as normal, use dynamic import like below
    import dynamic from 'next/dynamic';
    
    // Dynamically import the MasonryComponent if it's a heavy component
    const MasonryGallery = dynamic(
      () => import('@/app/Responsive/masonry-gallery/page'),
      {
        loading: () => <p>Loading gallery...</p>, // Fallback UI while loading
        ssr: false, // Optional: disables server-side rendering for this component
      }
    );
    
    export default function MasonryPage() {
      return (
        <div>
          <h1>Masonry Gallery</h1>
          <MasonryGallery />
        </div>
      );
    }

Production Build

  • Make sure to add the homepage URL to package.json like so "homepage": "https://<your-username>.github.io/<repo-name>/"
  • The only folder that is upload to the CDN is _next/static/ folder as everything else should not be exposed to the public

Deploying Static Next.js app to Github Pages

  • Github Pages only supports static exports of React App, some extra requirements are needed to run Next.js app on Github
    1. Update next.config.mjs file so that nextConfig object so it has the following:
    2. /* For Deploying to GitHub Pages as a static site (SSG) */
        output: 'export', // generate a static site
        distDir: 'out',   // where to put the generated production build
        images: {
          unoptimized: true, // Next.js Images component will not load in Github pages if it uses Next.js image optimization
        },
    3. Add a file named .nojekyll to the public/ folder; this prevent Github from building a jekyll app
  • The static build for the Next.js site will be in the folder out/ inside the root folder; this is what Github Page uses
    • The static version will not have API routes, dynamic server rendering, middleware, and getServerSideProps
  • The normal Next.js app build for the site will be in the folder .next/ inside the root folder