Published on

Necessary setup for a new Nextjs project

Nextjs project setup

  • Install Nextjs
  • Prettier
  • Husky, lint-stage and commit-lint
  • Env validation
  • Shadcn
  • Zod
  • React Query
  • types utils (isNilOrEmpty, isObjectWithKey etc)
  1. Install Nextjs
pnpm create next-app@latest my-app --yes
  1. Prettier
 pnpm install -D prettier eslint-config-prettier
// .prettierrc
{
  "semi": true,
  "singleQuote": true,
  "trailingComma": "all"
}

// in eslint.config.mjs (or .eslintrc)
{
  extends: ['next/core-web-vitals', 'prettier'],
},

// packages.json > scripts
"format": "prettier --write .",

  1. Husky, lint-stage and commit-lint
pnpm install -D husky lint-staged
npx husky init

// package.json > scripts
"prepare": "husky"

// commit lint
pnpm add -D @commitlint/cli @commitlint/config-conventional\n
// adding scripts: packages.json > scripts
"lint-staged": "lint-staged"

// modify .husky/pre-commit
pnpm run lint-staged

// .husky/_/commit-msg
#!/usr/bin/env sh
. "$(dirname "$0")/h"

// adding to package.json file:
 "lint-staged": {
    "*.ts": [
      "eslint --fix",
      "prettier --write"
    ]
  }
// commitlint.config.cjs
module.exports = {
  extends: ["@commitlint/config-conventional"],
  rules: {
    "subject-case": [
      2,
      "never",
      ["sentence-case", "start-case", "pascal-case"],
    ],
    "type-enum": [
      2,
      "always",
      ["feat", "fix", "docs", "style", "refactor", "test", "chore"],
    ],
  },
};

  1. Env validation
// env.ts
import { z } from "zod";

const serverEnvSchema = z.object({
  NODE_ENV: z.enum(["development", "test", "production"]),
});


const clientEnvSchema = z.object({
  NEXT_PUBLIC_API_URL: z.string().url(),
});

const serverEnv = serverEnvSchema.safeParse(process.env);
const clientEnv = clientEnvSchema.safeParse(process.env);

if (!serverEnv.success) {
  console.error(
    "❌ Invalid SERVER env:",
    serverEnv.error.flatten().fieldErrors,
  );
  throw new Error("Invalid server environment variables");
}

if (!clientEnv.success) {
  console.error(
    "❌ Invalid CLIENT env:",
    clientEnv.error.flatten().fieldErrors,
  );
  throw new Error("Invalid client environment variables");
}

export const env = {
  ...serverEnv.data,
  ...clientEnv.data,
};
// instrumentation.ts

import "@/env";

export function register() {
  // noop
}

  1. Shadcn
// Installation
npx shadcn@latest init

// Adding ui component
pnpm dlx shadcn@latest add button
  1. Zod
pnpm add zod
  1. React Query
pnpm add @tanstack/react-query
pnpm add -D @tanstack/eslint-plugin-query
// client provider
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

export const queryClient = new QueryClient();

export const ClientProvider = ({ children }: { children: React.ReactNode }) => {
  return (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );
};

  1. types utils
// isNilOrEmpty
export function isNilOrEmpty(value: any): boolean {
  // null or undefined
  if (value === null || value === undefined) return true;

  // string or array
  if (typeof value === "string" || Array.isArray(value)) {
    return value.length === 0;
  }

  // plain object (NOT array-like)
  if (typeof value === "object") {
    return Object.keys(value).length === 0;
  }

  return false;
}

// isObjectWithKey
export function isObjectWithKey(obj: any, key: string): boolean {
  return (
    !isNilOrEmpty(obj) &&
    typeof obj === "object" &&
    !Array.isArray(obj) &&
    Object.prototype.hasOwnProperty.call(obj, key)
  );
}