DEV Community

Cover image for Add personalized content to your docs with Markdoc, Next.js, and PropelAuth
propelauthblog for PropelAuth

Posted on • Originally published at propelauth.com

Add personalized content to your docs with Markdoc, Next.js, and PropelAuth

A few days ago, Stripe open sourced Markdoc 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:

// markdoc/tags.js

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

export const button = {
  render: Button,
};
Enter fullscreen mode Exit fullscreen mode
[//]: <> (pages/docs/example.md)

We can easily add React components to our markdown files:
{% button %}Button Text{% /button %}
Enter fullscreen mode Exit fullscreen mode

(based on this 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 or React. In my case, I used Next.js.

Then we can create our component:

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
Enter fullscreen mode Exit fullscreen mode

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

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

export const date = {
    render: DateComponent,
};
Enter fullscreen mode Exit fullscreen mode

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

# Date tag:

{% date /%}
Enter fullscreen mode Exit fullscreen mode

Date tag 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.

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

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

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
Enter fullscreen mode Exit fullscreen mode

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 to quickly turn this into an authenticated request either for a user:

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
Enter fullscreen mode Exit fullscreen mode

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

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])
Enter fullscreen mode Exit fullscreen mode

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

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)
}
Enter fullscreen mode Exit fullscreen mode

and you can follow either our Next.js guide or our documentation 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

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.

Discussion (0)