Add personalized content to your docs with Markdoc, Next.js, and PropelAuth
Tags: tutorial, javascript, markdown, react
Published: 2022-05-12 17:48:30

A few days ago, Stripe open sourced [Markdoc](https://markdoc.io/) which is a Markdown-based authoring framework. It’s used to power Stripe’s docs which is already a really strong selling point, but one of the first things that caught my eye was this:

```jsx
// markdoc/tags.js

import { Button } from '../components/Button';

export const button = {
  render: Button,
};
```

```markdown
[//]: <> (pages/docs/example.md)

We can easily add React components to our markdown files:
{% button %}Button Text{% /button %}
```

(based on [this](https://markdoc.io/docs/nextjs#tags) example) You can make your own React components and then pretty easily add them into your markdown files. Let’s look at some interesting ways we can use this:

### Using React hooks

We can start off by displaying the current time and using `useEffect` to keep it updating it. Start by following the instructions to set up a new project with [Next.js](https://markdoc.io/docs/nextjs) or [React](https://markdoc.io/docs/examples/react). In my case, I used Next.js.

Then we can create our component:

```jsx
import {useEffect, useState} from "react";

const DateComponent = () => {
    const [date, setDate] = useState(new Date())

    // Update the date every second
    useEffect(() => {
        const timer = setInterval(() => {
            setDate(new Date())
        }, 1000)

        // Clear the timer when we unmount
        return () => { clearInterval(timer) }
    }, [])

    return <div>{date.toLocaleString()}</div>
}

export default DateComponent
```

Afterwards, in `markdoc/tags.js`, we export a tag named `date` which references our `DateComponent`

```jsx
import DateComponent from "../components/DateComponent";

export const date = {
    render: DateComponent,
};
```

And finally, we can create a markdown file (`pages/docs/date-example.md`) and use the date tag:

```jsx
# Date tag:

{% date /%}
```

![Date tag gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v396arruw7hk5d9fzfgp.gif)

Pretty easy! If you wanted a more practical use case, you could do something like displaying the age of the post with [humanize-duration](https://www.npmjs.com/package/humanize-duration).

### Adding an API Key to your documentation

We can get fancier. Since our component can really do whatever we want, we can fetch information and display it directly in our docs.

One nice feature dev tools documentation often has is the user’s live API key embedded in the docs if they are logged in.

![api key docs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hke243f8ktppyyazzcfk.gif)

To do this, we can start by just making a fetch to a Next.js API route that we’ll create:

```jsx
import {useEffect, useState} from "react";

const ApiKey = () => {
    const [apiKey, setApiKey] = useState(null)

    useEffect(() => {
        fetchApiKey(accessToken).then(setApiKey)
    }, [])

    if (apiKey) {
        return <pre className="apiKey">{apiKey}</pre>
    } else {
        return <pre className="apiKey"><Loading/></pre>
    }
}

function fetchApiKey() {
    return fetch("/api/apikey", {
        method: "GET",
    }).then(res => res.text())

}

export default ApiKey
```

This doesn’t quite make sense though, because we haven’t added in any user information so we won’t know whose API key to fetch. We can use [PropelAuth](https://www.propelauth.com) to quickly turn this into an **authenticated** request either for a user:

```jsx
import {useAuthInfo, useRedirectFunctions} from "@propelauth/react";
import {useEffect, useState} from "react";

const ApiKey = () => {
    const {loading, isLoggedIn, accessToken} = useAuthInfo()
    const {redirectToLoginPage} = useRedirectFunctions()
    const [apiKey, setApiKey] = useState(null)

    // Check to see if they are logged in before we fetch
    useEffect(() => {
        if (accessToken) {
            fetchApiKey(accessToken).then(setApiKey)
        } else {
            setApiKey(null)
        }
    }, [accessToken])

    // New state: if they aren't logged in display a link
    //   to PropelAuth's hosted login screen so they can login
    if (apiKey) {
        return <pre className="apiKey">{apiKey}</pre>
    } else if (!loading && !isLoggedIn) {
        return <pre className="apiKey">
	          <a href="#" onClick={redirectToLoginPage}>Login</a> to view your API key
	      </pre>
    } else {
        return <pre className="apiKey"><Loading/></pre>
    }
}

// fetchApiKey now takes in an accessToken and passes it in the header
function fetchApiKey(accessToken) {
    return fetch("/api/apikey", {
        method: "GET",
        headers: {"Authorization": `Bearer ${accessToken}`}
    }).then(res => res.text())

}

export default ApiKey
```

or for an organization that the user is a member of:

```jsx
const {loading, isLoggedIn, accessToken, orgHelper} = useAuthInfo()

useEffect(() => {
    if (accessToken) {
        // Get the API key for an organization that the user is a member of
        const orgId = orgHelper.getSelectedOrg()
        fetchApiKey(orgId, accessToken).then(setApiKey)
    } else {
        setApiKey(null)
    }
}, [accessToken])
```

The API route we’ll create to support these use cases will look like:

```jsx
export default async function handler(req, res) {
    // Verifies that a valid accessToken is provided
    await requireUser(req, res);

    // req.user comes from requireUser
    const apiKey = await fetchApiKeyFromSecretManager(req.user.userId);
    res.status(200).send(apiKey)
}
```

and you can follow either our [Next.js guide](https://www.propelauth.com/post/fast-serverless-authentication-with-next-js-and-propelauth) or our [documentation](https://docs.propelauth.com/) to learn more and customize it for your use case.

Just like the `date` tag, we’ll need to add it to `markdoc/tags.js`, and create a markdown file to produce this:

![api key docs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hke243f8ktppyyazzcfk.gif)

### Summary

The ability to quickly and easily add arbitrary React components to a markdown file is really cool! It allows you to be really creative and easily add dynamic content to your markdown files.