NextJs + S3
A collection of notes for issues, workarounds, and solutions I've found building emerickbosch.com as a static site using S3 website hosting and NextJs.
See actual source code on the Github repo.
.html files not matching paths
Problem
Paths are like /article/foo
, but the actual html is /article/foo.html
. When navigating to /article/foo
in the bucket, the file is not found.
Solution
Drop the .html
from the S3 object key to make it match the path exactly.
/**
* Given a relative filename, returns the S3 object key.
*/
function removeHtmlSuffix(fileName: string) {
// root files that bucket hosting points to
if (fileName.endsWith("index.html") || fileName.endsWith("404.html")) {
return fileName;
}
if (fileName.endsWith(".html")) {
return fileName.substring(0, fileName.length - ".html".length);
}
return fileName;
}
When uploading, it's important to tell S3 what the content-type of the file is.
import mime from "mime-types";
import { S3 } from "@aws-sdk/client-s3";
const DIST_FOLDER = "/your/folder/with/static/website";
async function upload(filePath) {
const key = removeHtmlSuffix(path.relative(DIST_FOLDER, filePath));
const opts = {
Key: key,
Bucket: config.bucketName,
Body: createReadStream(filePath),
ContentType: mime.lookup(filePath) || undefined,
};
const s3 = new S3(/* your S3 config */);
await s3.putObject(opts);
}
Permalinks
Problem
I want to have RSS feeds which point to articles using permalinks. I also want to be able change my titles and slugs (my notes are living documents).
So I cannot count on slugs as permalinks; I need another path.
Solution
Add UIDs to the frontmatter of my articles.
---
title: NextJs + S3
guid: a006ac19-d6f4-498a-8210-51188fabe328
---
Use those UIDs as slugs when generating static pages.
export const generateStaticParams = async () => {
const slugs = [];
for (const metadata of getMarkdownMetadata()) {
slugs.push({ slug: metadata.slug });
slugs.push({ slug: metadata.id }); // permalinks for each article; allows titles to change
}
return slugs;
};
Redirect to the real articles in those pages.
const Article = ({ params }: Props) => {
const slug = params.slug;
const permalinkedArticle = getMarkdownMetadata().find(
({ id }) => slug === id,
);
if (permalinkedArticle) {
return (
<ArticleRedirect articlePath={`/articles/${permalinkedArticle.slug}`} />
);
}
// the rest of the component
}
Code blocks
rehype-prism-plus
: syntax, line numbers, line highlightingrehype-code-titles
: code block titles
Notes:
rehype-mdx-code-props
is not compatible withrehype-prism-plus
.