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:
NEXT_PUBLIC_DOMAIN="http://localhost:3000"
Create a file called env.local
, add the following environment variables, .env.local
takes priority over .env.development
.
# 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.
Learn more about environment variables in the Next.js docs
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.
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;
Import environment variables to constants.ts
to minimise process.env
declarations throughout the codebase.
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.
{
"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.
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]],
},
});
Learn more about contentLayer's document types here
Data
Data configuration files are located in the following directory: src/data
.
Data files are written in .ts
files.
Header links
To configure the header links (links that appear on the header), edit the file: links.ts
, it's content look like this:
const links = [
{ href: "/about", label: "About" },
...
];
Footer links
The footer contains 3 columns that you can add or remove links to. You'll definitely want to replace the defaults with your own.
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.
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.
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} />
}
Learn more about MDX components here
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.
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:
export const rehypePrettyCodeOptions = {
theme: "dark-plus",
tokensMap: {
fn: "entity.name.function",
},
defaultLang: "plaintext",
};
All themes can be found here.