---
title: "19 — Tooling and Configuration — TypeScript, ESLint, Prettier, Env Vars"
uid: tooling-config
tags: ["roadmap:nextjs", "eslint", "typescript", "prettier", "environment-variables", "nextjs"]
excerpt: "TypeScript types, ESLint catches, Prettier formats, env vars separate config from code — and Next.js wires all four with sensible defaults."
date: 2026-08-13T03:27:59+0000
source: https://www.aveshina.my.id/en/blog/tooling-config
---

The tooling stack around a project used to be something I assembled from memory. The model that clicked: **TypeScript adds static typing on top of JavaScript, ESLint catches problematic patterns, Prettier formats code, and environment variables separate config from code** [1][2][3][4]. Next.js wires all four with sensible defaults, so the project starts typed, linted, formatted, and configurable.

## TypeScript — static typing, built in

TypeScript is a syntactic superset of JavaScript that adds static typing [1]. Next.js ships with built-in TypeScript: create-next-app installs the necessary packages and configures tsconfig.json automatically. Adding TypeScript to an existing project is as simple as renaming a file to .ts/.tsx and running next dev — Next.js installs the deps and writes the recommended config.

The payoff is the type errors caught before runtime, the editor autocomplete driven by real types, and the refactors that are actually safe. For any project of meaningful size, the cost of writing types is paid back many times over in bugs not shipped.

```
// types flow through everything, including server function params
export async function createPost(input: { title: string }): Promise<Post> {
  return db.post.create({ data: input });
}
```

## ESLint — catching problems

ESLint is an open-source static analysis tool that identifies problematic patterns and style issues in JavaScript/TypeScript code [2]. Next.js provides eslint-plugin-next, bundled into the base configuration, which catches Next.js-specific issues — unoptimized images, incorrect next/link usage, missing alt text, the next/head mistakes that hurt SEO.

```
next lint  # runs the configured ESLint setup
```

The plugin turns "framework gotchas you'd discover at runtime" into "lint errors you see before you push." That shift-left is the whole value.

## Prettier — formatting

Prettier is an opinionated code formatter that enforces a consistent style across the project [3]. It supports JavaScript, TypeScript, CSS, and more. The potential conflict: ESLint's formatting rules can collide with Prettier's. The fix is eslint-config-prettier, which disables ESLint's formatting rules so Prettier owns formatting and ESLint owns code-quality checks [3].

```
// .eslintrc — let Prettier own formatting
{
  "extends": ["next/core-web-vitals", "prettier"]
}
```

The division of labor worth internalizing: **ESLint catches bugs and enforces conventions; Prettier formats.** Don't make them fight.

## Environment variables — config outside code

Environment variables are dynamic values that affect program behavior — typically configuration, API keys, and secrets that shouldn't be hardcoded [4]. Next.js has built-in support: a .env file loads variables, and the NEXT_PUBLIC_ prefix bundles a variable into the browser bundle.

```
# .env.local
DATABASE_URL=postgres://...        # server-only
NEXT_PUBLIC_GA_ID=G-XXXX           # exposed to the browser
```

```
// server-only — never reaches the browser
const db = connect(process.env.DATABASE_URL);

// client-safe — bundled into the browser
<Script src={`https://...?id=${process.env.NEXT_PUBLIC_GA_ID}`} />
```

The distinction is the security model: anything without NEXT_PUBLIC_ stays on the server and is safe to use for secrets. Anything with the prefix is inlined into client bundles and visible to anyone who inspects them — so it's only for non-secret public config.

```figure
<svg viewBox="0 0 720 260" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="The tooling stack as a pipeline. Source code flows through TypeScript (types), ESLint (catch problems), Prettier (format), and is configured by environment variables (.env, with NEXT_PUBLIC_ prefix for browser-safe values).">
  <defs>
    <marker id="tarrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
      <path d="M0,0 L10,5 L0,10 z" fill="#64748b"/>
    </marker>
  </defs>
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">

    <rect x="30" y="100" width="100" height="44" rx="8" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.5"/>
    <text x="80" y="126" font-size="11" font-weight="700" fill="#1e1b4b" text-anchor="middle">source</text>

    <path d="M130,122 L168,122" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#tarrow)"/>

    <rect x="170" y="100" width="100" height="44" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="220" y="120" font-size="11" font-weight="700" fill="#052e16" text-anchor="middle">TypeScript</text>
    <text x="220" y="136" font-size="9" fill="#475569" text-anchor="middle">static types</text>

    <path d="M270,122 L308,122" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#tarrow)"/>

    <rect x="310" y="100" width="100" height="44" rx="8" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="360" y="120" font-size="11" font-weight="700" fill="#422006" text-anchor="middle">ESLint</text>
    <text x="360" y="136" font-size="9" fill="#475569" text-anchor="middle">catch problems</text>

    <path d="M410,122 L448,122" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#tarrow)"/>

    <rect x="450" y="100" width="100" height="44" rx="8" fill="#fce7f3" stroke="#db2777" stroke-width="1.5"/>
    <text x="500" y="120" font-size="11" font-weight="700" fill="#500724" text-anchor="middle">Prettier</text>
    <text x="500" y="136" font-size="9" fill="#475569" text-anchor="middle">format</text>

    <path d="M550,122 L588,122" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#tarrow)"/>

    <rect x="590" y="100" width="100" height="44" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="640" y="126" font-size="11" font-weight="700" fill="#052e16" text-anchor="middle">build</text>

    <!-- env vars below -->
    <rect x="310" y="180" width="240" height="50" rx="8" fill="#fee2e2" stroke="#dc2626" stroke-width="1.5"/>
    <text x="430" y="200" font-size="11" font-weight="700" fill="#7f1d1d" text-anchor="middle">.env files</text>
    <text x="430" y="218" font-size="9" fill="#7f1d1d" text-anchor="middle">DATABASE_URL (server) · NEXT_PUBLIC_* (browser)</text>
  </g>
</svg>
```

## How I use this

TypeScript is non-negotiable on any new project — create-next-app with TypeScript selected, strict mode on. ESLint with eslint-plugin-next runs locally and in CI. Prettier owns formatting via eslint-config-prettier, so the two tools don't fight. Environment variables live in .env.local for development and in the hosting provider's environment settings for production, with the NEXT_PUBLIC_ discipline kept tight: secrets never get the prefix. The combination gives a project that's typed, linted, formatted, and configurable from day one.

## References

[1] Vercel, "TypeScript," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/app/api-reference/config/typescript](https://nextjs.org/docs/app/api-reference/config/typescript)

[2] Vercel, "ESLint plugin," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/app/api-reference/config/eslint](https://nextjs.org/docs/app/api-reference/config/eslint)

[3] Vercel, "ESLint plugin with Prettier," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/app/api-reference/config/eslint#with-prettier](https://nextjs.org/docs/app/api-reference/config/eslint#with-prettier)

[4] Vercel, "How to use environment variables in Next.js," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/app/guides/environment-variables](https://nextjs.org/docs/app/guides/environment-variables)

[5] Prettier, "eslint-config-prettier," GitHub, 2024. [Online]. Available: [https://github.com/prettier/eslint-config-prettier](https://github.com/prettier/eslint-config-prettier)

[6] Vercel, "Configuration," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/app/api-reference/config](https://nextjs.org/docs/app/api-reference/config)

```quiz
Q: How do you add TypeScript to an existing Next.js project?
- Install typescript and configure tsconfig.json manually
- Rename a file to .ts/.tsx and run next dev — Next.js installs deps and writes the recommended config
correct: 1
explain: Next.js has built-in TypeScript. Renaming a file and running next dev triggers automatic dependency installation and tsconfig.json generation.

Q: What does eslint-plugin-next provide?
- A faster build
- Next.js-specific lint rules that catch common framework mistakes (unoptimized images, incorrect next/link usage, etc.)
correct: 1
explain: The bundled plugin catches Next.js gotchas — turning runtime surprises into lint errors seen before pushing.

Q: How do you prevent ESLint and Prettier from conflicting over formatting?
- Disable ESLint entirely
- Add eslint-config-prettier, which turns off ESLint's formatting rules so Prettier owns formatting and ESLint owns code quality
correct: 1
explain: eslint-config-prettier disables ESLint's formatting rules. The division of labor: ESLint catches bugs, Prettier formats.

Q: What does the NEXT_PUBLIC_ prefix on an environment variable do?
- Marks it as a secret
- Bundles the variable into the browser bundle, exposing it to the client — only for non-secret public config
correct: 1
explain: Variables without the prefix stay server-side and are safe for secrets. NEXT_PUBLIC_ variables are inlined into client bundles and visible to anyone — only for public values.

Q: A database connection string should be…
- prefixed with NEXT_PUBLIC_ so the client can use it
- kept server-only, never prefixed — it's a secret
correct: 1
explain: Secrets like DATABASE_URL stay server-side. The NEXT_PUBLIC_ prefix would expose them in the browser bundle, which is a security hole.
```
