R
Rishtaara
Next.js: Zero to Full-Stack
Lesson 39 of 42Article17 min

Configuring: MDX, Draft Mode, Security Headers, ESLint

MDX lets you write Markdown with React components inside. Great for docs and lesson content. Use @next/mdx or a content layer that compiles MDX at build time.

MDX in Next.js

MDX lets you write Markdown with React components inside. Great for docs and lesson content. Use @next/mdx or a content layer that compiles MDX at build time.

Real-life example: MDX is a recipe card that can also include a working timer widget — words plus interactive pieces on one page.

next.config.mdx sketch
import createMDX from "@next/mdx";

const withMDX = createMDX({
  extension: /\.mdx?$/,
});

const nextConfig = {
  pageExtensions: ["ts", "tsx", "md", "mdx"],
};

export default withMDX(nextConfig);

Draft Mode

Draft Mode lets CMS editors preview unpublished content. You enable it from a Route Handler that calls draftMode().enable(), then fetch draft data when isEnabled is true.

Real-life example: Draft Mode is a backstage pass — editors see the unfinished show; the public ticket holders wait for opening night.

Enable draft mode + read it
// app/api/draft/route.ts
import { draftMode } from "next/headers";
import { redirect } from "next/navigation";

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const secret = searchParams.get("secret");
  const slug = searchParams.get("slug");

  if (secret !== process.env.DRAFT_SECRET || !slug) {
    return new Response("Invalid", { status: 401 });
  }

  const draft = await draftMode();
  draft.enable();
  redirect(`/blog/${slug}`);
}

// in a page:
import { draftMode } from "next/headers";

export default async function PostPage() {
  const { isEnabled } = await draftMode();
  const post = await getPost({ draft: isEnabled });
  return <article>{/* ... */}</article>;
}

Security headers, ESLint, static files

You already saw security headers in the JWT lesson — keep them in next.config for every response.

ESLint with eslint-config-next catches App Router mistakes (missing keys, bad hooks). Run next lint in CI.

Static files live in public/ and are served from /. public/logo.svg → /logo.svg. Do not put secrets there.

Real-life example: public/ is the pamphlet rack in the lobby — anyone can take a brochure; do not leave the office safe combination on that rack.

  • Headers — clickjacking, MIME sniffing, referrer, permissions
  • ESLint — fail CI on errors for shared repos
  • public/ — images, favicons, robots manual files if needed
Tip: Prefer app/robots.ts and app/sitemap.ts over hand-written files in public/ so URLs stay generated from your data.
ESLint check
npx next lint
# or
npm run lint