Writings Static Website with Next JS Setting up Velite for Processing Markdown Content Through MDX Adding Velite to the NextJS, React with Radix UI and Shadcn UI project and configuring blog source for markdown using MDX
We use velite
for accessing the content of the MDX files in JavaScript.
# npm i velite --save
We store the MDX content for the blog read by Velite in a /content/blog
directory. Each year has its own directory. The file name starts with the month and day. This structure is not mandatory, but rather to help organize and manage the blog. The directory structure looks like this:
...
├── components.json
├── content
│ └── blog
│ ├── 2023
│ │ └── 06-21-summer-solstice.mdx
│ └── 2024
│ └── 06-21-summer-solstice.mdx
├── next-env.d.ts
...
In order to allow the code to access the cotent directory, the following is added in tsconfig.json
:
{
"compilerOptions" : {
...
"paths" : {
"@/*" : [
"./src/*"
],
"#site/content" : [
"./.velite"
]
}
},
"include" : [
"next-env.d.ts" ,
"**/*.ts" ,
"**/*.tsx" ,
".next/types/**/*.ts"
],
"exclude" : [
"node_modules"
]
}
Velite configuration is placed in next.config.mjs
:
/** @type {import('next').NextConfig} */
import { build } from 'velite' ;
class VeliteWebpackPlugin {
static started = false ;
constructor ( /** @type {import('velite').Options} */ options = {}) {
this .options = options;
}
apply ( /** @type {import('webpack').Compiler} */ compiler ) {
// executed three times in nextjs !!!
// twice for the server (nodejs / edge runtime) and once for the client
compiler.hooks.beforeCompile. tapPromise ( 'VeliteWebpackPlugin' , async () => {
if (VeliteWebpackPlugin.started) return ;
VeliteWebpackPlugin.started = true ;
const dev = compiler.options.mode === 'development' ;
this .options.watch = this .options.watch ?? dev;
this .options.clean = this .options.clean ?? ! dev;
await build ( this .options); // start velite
});
}
}
const nextConfig = {
output: 'export' ,
// other next config here...
webpack : ( config ) => {
config.plugins. push ( new VeliteWebpackPlugin ());
return config;
},
};
export default nextConfig;
Velite creates temporary and cache files as it works. Those files do not need to be stored in git, so we amend /.gitignore
to include:
...
# local env files
.env*.local
# vercel
.vercel
# velite files
.velite
# typescript
*.tsbuildinfo
next-env.d.ts
The source content for the blog and the frontmatter properties that are being retrieved are specified in velite.config.ts
:
import { defineConfig, defineCollection, s } from 'velite' ;
const computedFields = < T extends { slug : string }>( data : T ) => ({
... data,
slugAsParams: data.slug. split ( '/' ). slice ( 1 ). join ( '/' ),
});
const posts = defineCollection ({
name: 'Post' ,
pattern: 'blog/**/*.mdx' ,
schema: s
. object ({
slug: s. path (),
title: s. string (). max ( 99 ),
description: s. string (). max ( 999 ). optional (),
date: s. isodate (),
published: s. boolean (). default ( true ),
body: s. mdx (),
})
. transform (computedFields),
});
export default defineConfig ({
root: 'content' ,
output: {
data: '.velite' ,
assets: 'public/static' ,
base: '/static/' ,
name: '[name]-[hash:6].[ext]' ,
clean: true ,
},
collections: { posts },
mdx: {
rehypePlugins: [],
remarkPlugins: [],
},
});
When we run
# npm run dev
we see that Velite is running:
▲ Next.js 14.2.4
- Local: http://localhost:3000
✓ Starting...
[VELITE] build finished in 140.94ms
[VELITE] watching for changes in 'content'
✓ Ready in 1269ms
<w> [webpack.cache.PackFileCacheStrategy/webpack.FileSystemInfo] Parsing of /private/var/prj/static-nextjs/node_modules/velite/dist/velite-DLg1KhQl.js for build dependencies failed at 'import(configUrl.href)'.
<w> Build dependencies behind this expression are ignored and might cause incorrect cache invalidation.
Velite creates a working directory .velite
where it stores cached and temporary files:
...
├── .gitignore
├── .velite
│ ├── index.d.ts
│ ├── index.js
│ └── posts.json
├── README.md
├── components.json
...
Last Updated: July 12, 2024