Configuration

ZippyStarter comes pre-configured so you can focus on what's important, but the config can be changed, this page shows you how to do that.

Environment variables

There's an .env.development file in the repository which includes:

.env.development
NEXT_PUBLIC_DOMAIN="http://localhost:3000"

Create a file called env.local, add the following environment variables, .env.local takes priority over .env.development.

.env.local
# Mandatory: The url of your website, used in metadataBase
NEXT_PUBLIC_DOMAIN="https://yourwebaddress.com"
 
# Optional: Used for displaying the last time your repo changed
GITHUB_TOKEN="your token"
GITHUB_REPO_URL="https://api.github.com/repos/account/repo"
 
# Optional: Used with nodemailer for sending emails from an email address
EMAIL_ADDRESS=""
EMAIL_PASSWORD=""
EMAIL_HOST=""

You will also need to add the same environment variables, with production values, into the settings for your Vercel deployment.

Constants

The constants.ts file is used for sharing variables throughout the codebase, they're always SCREAMING_SNAKE_CASE so you can tell them apart from other variables.

constants.ts
export const DEFAULT_CURRENCY = "USD";
export const DEFAULT_LOCALE = "en-US";
export const DOMAIN = process.env.NEXT_PUBLIC_DOMAIN ?? "http://localhost:3000";
export const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
export const GITHUB_REPO_URL = process.env.GITHUB_REPO_URL;
export const STRIPE_LINK = "https://buy.stripe.com/[yourid]";
export const SPOTS = 20;

Config

There's a minimal amount of configuration in config.json for populating the default author used in blog posts, and default image sizes used with featured images via the CLI.

config.json
{
  "defaultAuthorName": "Author name",
  "defaultAvatarPath": "/authors/author.webp",
  "blogRoot": "content/blog",
  "blogPath": "blog",
  "featuredImage": {
    "width": 1232,
    "height": 620
  }
}

ContentLayer

ContentLayer is configured via contentLayer.config.ts. It's setup so that if you want new document types, you create and add to src/contentlayer, import and add them to the documentTypes array, like the others have been.

contentLayer.config.ts
import { makeSource } from "contentlayer/source-files";
import remarkGfm from "remark-gfm";
import rehypeSlug from "rehype-slug";
import rehypePrettyCode from "rehype-pretty-code";
import { rehypePrettyCodeOptions } from "./src/utils/rehype/rehypePrettyCode";
import Blog from "./src/contentlayer/Blog";
import Page from "./src/contentlayer/Page";
import Doc from "./src/contentlayer/Doc";
 
export default makeSource({
  contentDirPath: "content",
  documentTypes: [Blog, Page, Doc],
  mdx: {
    remarkPlugins: [remarkGfm],
    rehypePlugins: [rehypeSlug, [rehypePrettyCode, rehypePrettyCodeOptions]],
  },
});

Data

Data configuration files are located in the following directory: src/data. Data files are written in .ts files.

To configure the header links (links that appear on the header), edit the file: links.ts, it's content look like this:

@/src/data/links
const links = [
  { href: "/about", label: "About" },
  ...
];

The footer contains 3 columns that you can add or remove links to. You'll definitely want to replace the defaults with your own.

@/src/data/links
const links = [
  { href: "/about", label: "About" },
  ...
];
 
const termsLinks = [
  { href: "/terms", label: "Terms" },
  ...
];
 
const socialLinks = [
  { href: "https://www.linkedin.com/in/morgan--feeney/", label: "LinkedIn" },
  ...
];

Work History

This array is used to populate the work history section of the about page.

const workHistory = [
  {
    year: "2022 - present",
    title: "Lead Designer & Developer at RebelTech",
    description:
      "At RebelTech, I'm the linchpin connecting the design and development teams. I've led projects that shook up conventional industry standards, and I've introduced design systems that are now adopted company-wide.",
  },
  ...
]

MDX components

The components used by MDX can be mapped to a single object, which can be used globally in all files, or on a per-file basis.

@/components/mdx/MdxComponents
import Link from "next/link";
import NextImg from "@/components/NextImg";
import { H2, H3, H4, H5 } from "@/components/Headings";
import { Table } from "@/components/Table";
import Blockquote from "@/components/mdx/BlockQuote";
 
const MdxComponents = {
  NextImg,
  Link,
  a: Link,
  h2: H2,
  h3: H3,
  h4: H4,
  h5: H5,
  table: Table,
  blockquote: Blockquote,
};
 
export default MdxComponents;

This mechanism allows to you assign custom React components to native HTML elements, and include other React components, whilst retaining consistency throughout the codebase.

@/components/templates/blog/BlogTemplate
import { getMDXComponent } from "next-contentlayer/hooks";
import MdxComponents from "@/components/mdx/MdxComponents";
 
const BlogTemplate = ({ post }) => {
  const MDXContent = getMDXComponent(post.body.code ?? "");
 
  return <MDXContent components={MdxComponents as any} />
}

Potential errors

If you try to use a React Component without first passing it to <MDXContent />, they won't be available to the MDX file, and you'll get an unhandled error.

Example error output
Error: Expected component `Test` to be defined: you likely forgot to import, pass, or provide it.
It’s referenced in your code at `40:1-40:8` in `/Users/yourname/zippystarter/content/docs/getting-started/_mdx_bundler_entry_point-dbd3d34e-98a9-4a65-b0dc-5ca3071d27b3.mdx`

If you try to import a React component directly into an MDX file, you'll also get an error. Sometimes your IDE will do this for you, and be really unhelpful.

Extending

As a general rule you'll want to use the same MDX components throughout the codebase, but there could be a particular area of your website that needs customisation and you don't want to add to the global object.

In those cases, you can import the global object, spread it into a new object, and add more components.

import { getMDXComponent } from "next-contentlayer/hooks";
import MdxComponents from "@/components/mdx/MdxComponents";
 
const BlogTemplate = ({ post }) => {
  const MDXContent = getMDXComponent(post.body.code ?? "");
 
  const mdxComponents = {
    ...MdxComponents,
    blockquote: Blockquote,
  };
 
  return <MDXContent components={mdxComponents as any} />
}

Passing new classes

All components accept a className attribute, this is so you can re-use components without applying styles that will only work in one location, globally.

A potential scenario is were you want to add an additional class to a heading that gets passed to MDX, here's how you would do that:

import { getMDXComponent } from "next-contentlayer/hooks";
 
const BlogTemplate = ({ post }) => {
  const MDXContent = getMDXComponent(post.body.code ?? "");
 
  const mdxComponents = {
    ...MdxComponents,
      h2: (props) => <H2 className="my-new-class">{props.children}</H2>,
  };
 
  return <MDXContent components={mdxComponents as any} />
}

Syntax highlighting

Under the hood ZippyStarter uses Rehype Pretty Code — their docs are pretty extensive. To configure the theme you want, navigate to the following file and change the theme value:

@/utils/rehype/rehypePrettyCode.ts
export const rehypePrettyCodeOptions = {
  theme: "dark-plus",
  tokensMap: {
    fn: "entity.name.function",
  },
  defaultLang: "plaintext",
};