We are working on Code Hike v1.0

Installation

Code Hike is a remark plugin for MDX v2. The specific set up will depend on your stack, it usually involves five steps:

1. Set up MDX v2
2. Install the Code Hike plugin
3. Include Code Hike's CSS
4. Add the remark plugin to your MDX configuration
5. Copy and try one of the demos to test the set up.

Frameworks

Installation guides for specific frameworks. Pick one:

Next.js + Contentlayer + Code Hike

Contentlayer is a content preprocessor that transforms MDX files into type-safe JSON you can easily import into your application.

A demo of Code Hike + Contentlayer is available on GitHub. You can also try it out from your browser on StackBlitz.

We are going to use it together with Next.js, so let's start by installing it:


npm install react react-dom next

We also need the dependencies for Contentlayer and its plugin:


npm install contentlayer next-contentlayer

And, of course, we need Code Hike:


npm install @code-hike/mdx

posts/one.mdx
posts/two.mdx

---
title: Post one
---
This is the first post.
```python hello.py
# mark[16:24]
print("This is Code Hike")
```

./posts

We are going to build a very minimal blog. Let's start with the content.

Create a ./posts/ folder with some files in it. For example, posts/one.mdx and posts/two.mdx.

We can add some metadata, like a title, using frontmatter. Contentlayer will understand that out of the box.

posts/one.mdx
next.config.js

const {
withContentlayer,
} = require("next-contentlayer")
module.exports = withContentlayer({})

next.config.js

Then create a next.config.js file at the root of your project.

Here we tell Next.js that we want to use Contentlayer using the withContentlayer plugin from "next-contentlayer".

The {} parameter is the Next.js configuration object. So if you need to set any option, you can do it there.

posts/one.mdx
contentlayer.config.js

import {
defineDocumentType,
makeSource,
} from "contentlayer/source-files"
const Post = defineDocumentType(() => ({
name: "Post",
filePathPattern: `**/*.mdx`,
contentType: "mdx",
}))
export default makeSource({
contentDirPath: "posts",
documentTypes: [Post],
})

contentlayer.config.js

Contentlayer lets you have different types of content, each with its own schema. In our case, we'll have only one type of content: posts.

Create a configuration file for Contentlayer at the root of your project, and use defnineDocumentType and makeSource to define that the content comes from the ./posts folder, that it should be treated as mdx.

posts/one.mdx
contentlayer.config.js

import {
defineDocumentType,
makeSource,
} from "contentlayer/source-files"
import { remarkCodeHike } from "@code-hike/mdx"
const Post = defineDocumentType(() => ({
name: "Post",
filePathPattern: `**/*.mdx`,
contentType: "mdx",
}))
export default makeSource({
contentDirPath: "posts",
documentTypes: [Post],
mdx: {
remarkPlugins: [
[remarkCodeHike, { theme: "nord" }],
],
},
})

Now we add Code Hike configuration.

Import remarkCodeHike and pick a theme For more information about themes, see the themes docs.

Then add the Code Hike plugin to the mdx options. You can pass more options inside the configuration object if you need to, see the configuration docs.

posts/one.mdx
contentlayer.config.js

import {
defineDocumentType,
makeSource,
} from "contentlayer/source-files"
import { remarkCodeHike } from "@code-hike/mdx"
const Post = defineDocumentType(() => ({
name: "Post",
filePathPattern: `**/*.mdx`,
contentType: "mdx",
fields: {
title: {
type: "string",
required: true,
},
},
computedFields: {
url: {
type: "string",
resolve: (doc) =>
`/posts/${doc._raw.flattenedPath}`,
},
},
}))
export default makeSource({
contentDirPath: "posts",
documentTypes: [Post],
mdx: {
remarkPlugins: [
[remarkCodeHike, { theme: "nord" }],
],
},
})

There's one more thing we need to add to the contentlayer config: information about the document metadata.

We add two fields:

One for the title, that comes directly from the post frontmatter.

And another one for the URL, this one is computed from the path of the file.

contentlayer.config.js
jsconfig.json

{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"contentlayer/generated": [
"./.contentlayer/generated"
]
}
}
}

jsconfig.json

We need to create one more config file.

Contentlayer generates files in the .contentlayer/generated folder. To import those files, we need a jsconfig.json file that tells the compiler where to look for the generated files.

If you are using typescript, you need the same but for the tsconfig.json file.

contentlayer.config.js
pages/_app.js

import "@code-hike/mdx/dist/index.css"
function MyApp({
Component,
pageProps,
}) {
return <Component {...pageProps} />
}
export default MyApp

pages/_app.js

Then we use pages/_app.js file to import Code Hike's stylesheet.

You can find more information about the _app.js file in the Next.js official docs.

If you want to customize Code Hike's styles with a global stylesheet make sure to import it after this import to avoid specificity issues.

You can learn more about customizing Code Hike styles in the styling docs.

contentlayer.config.js
pages/index.js

import Link from "next/link"
import {
allPosts
} from "contentlayer/generated"
export async function getStaticProps() {
return { props: { posts: allPosts } }
}
export default function Home({
posts,
}) {
return (
<div>
<h1>A Blog</h1>
</div>
)
}

pages/index.js

Now we can add a page to our Next.js app.

In pages/index.js, we want to show a list of all the posts. So we import allPosts from the generated files, and pass it as a prop to the page using getStaticProps.

The posts array will be something like this:


[
{
title: "Post one",
_id: "one.mdx",
_raw: { ... },
body: { ... },
type: "Post",
url: "/posts/one",
},
{
title: "Post two",
...
},
]

contentlayer.config.js
pages/index.js

import Link from "next/link"
import {
allPosts
} from "contentlayer/generated"
export async function getStaticProps() {
return { props: { posts: allPosts } }
}
export default function Home({
posts,
}) {
return (
<div>
<h1>A Blog</h1>
Posts:
<ul>
{posts.map((post) => (
<li key={post._id}>
<Link href={post.url}>
<a>{post.title}</a>
</Link>
</li>
))}
</ul>
</div>
)
}

Now we render the lists of posts using the fields we defined in the document type: the post url and title.

If you run the app now, you should see the following:

contentlayer.config.js
pages/posts/[slug].js

import { allPosts } from "contentlayer/generated"
import { useMDXComponent } from "next-contentlayer/hooks"
export async function getStaticPaths() {
const paths = allPosts.map(
(post) => post.url
)
return { paths, fallback: false }
}
export async function getStaticProps({
params,
}) {
const post = allPosts.find(
(post) =>
post._raw.flattenedPath ===
params.slug
)
return { props: { post } }
}
const PostLayout = ({ post }) => {
const MDXContent = useMDXComponent(
post.body.code
)
return (
<article style={{ maxWidth: 600 }}>
<h1>{post.title}</h1>
<MDXContent />
</article>
)
}
export default PostLayout

pages/posts/[slug].js

To render a specific post, we use Next.js dynamic routes and create a [slug].js page inside the pages/posts/ folder (the name of the posts folder should be the same that we used when we created the url field).

We use getStaticPaths to tell Next.js the post URLs, and getStaticProps to pass the post data to the page.

pages/posts/[slug].js
posts/one.mdx

import { allPosts } from "contentlayer/generated"
import { useMDXComponent } from "next-contentlayer/hooks"
export async function getStaticPaths() {
const paths = allPosts.map(
(post) => post.url
)
return { paths, fallback: false }
}
export async function getStaticProps({
params,
}) {
const post = allPosts.find(
(post) =>
post._raw.flattenedPath ===
params.slug
)
return { props: { post } }
}
const PostLayout = ({ post }) => {
const MDXContent = useMDXComponent(
post.body.code
)
return (
<article style={{ maxWidth: 600 }}>
<h1>{post.title}</h1>
<MDXContent />
</article>
)
}
export default PostLayout
//

Then we finally render the post.

We use the useMDXComponent hook from Contentlayer. It transforms the post.body.code into a React component.

Now if we open the post in the browser, we should see the following:

./posts

We are going to build a very minimal blog. Let's start with the content.

Create a ./posts/ folder with some files in it. For example, posts/one.mdx and posts/two.mdx.

We can add some metadata, like a title, using frontmatter. Contentlayer will understand that out of the box.

next.config.js

Then create a next.config.js file at the root of your project.

Here we tell Next.js that we want to use Contentlayer using the withContentlayer plugin from "next-contentlayer".

The {} parameter is the Next.js configuration object. So if you need to set any option, you can do it there.

contentlayer.config.js

Contentlayer lets you have different types of content, each with its own schema. In our case, we'll have only one type of content: posts.

Create a configuration file for Contentlayer at the root of your project, and use defnineDocumentType and makeSource to define that the content comes from the ./posts folder, that it should be treated as mdx.

Now we add Code Hike configuration.

Import remarkCodeHike and pick a theme For more information about themes, see the themes docs.

Then add the Code Hike plugin to the mdx options. You can pass more options inside the configuration object if you need to, see the configuration docs.

There's one more thing we need to add to the contentlayer config: information about the document metadata.

We add two fields:

One for the title, that comes directly from the post frontmatter.

And another one for the URL, this one is computed from the path of the file.

jsconfig.json

We need to create one more config file.

Contentlayer generates files in the .contentlayer/generated folder. To import those files, we need a jsconfig.json file that tells the compiler where to look for the generated files.

If you are using typescript, you need the same but for the tsconfig.json file.

pages/_app.js

Then we use pages/_app.js file to import Code Hike's stylesheet.

You can find more information about the _app.js file in the Next.js official docs.

If you want to customize Code Hike's styles with a global stylesheet make sure to import it after this import to avoid specificity issues.

You can learn more about customizing Code Hike styles in the styling docs.

pages/index.js

Now we can add a page to our Next.js app.

In pages/index.js, we want to show a list of all the posts. So we import allPosts from the generated files, and pass it as a prop to the page using getStaticProps.

The posts array will be something like this:


[
{
title: "Post one",
_id: "one.mdx",
_raw: { ... },
body: { ... },
type: "Post",
url: "/posts/one",
},
{
title: "Post two",
...
},
]

Now we render the lists of posts using the fields we defined in the document type: the post url and title.

If you run the app now, you should see the following:

pages/posts/[slug].js

To render a specific post, we use Next.js dynamic routes and create a [slug].js page inside the pages/posts/ folder (the name of the posts folder should be the same that we used when we created the url field).

We use getStaticPaths to tell Next.js the post URLs, and getStaticProps to pass the post data to the page.

Then we finally render the post.

We use the useMDXComponent hook from Contentlayer. It transforms the post.body.code into a React component.

Now if we open the post in the browser, we should see the following:

posts/one.mdx
posts/two.mdx

---
title: Post one
---
This is the first post.
```python hello.py
# mark[16:24]
print("This is Code Hike")
```

Again, a demo of Code Hike + Contentlayer is available on GitHub. You can also try it out from your browser on StackBlitz.