Back to Withastro

Add reading time

src/content/docs/en/recipes/reading-time.mdx

latest3.7 KB
Original Source

import { Steps } from '@astrojs/starlight/components'; import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';

Create a remark plugin which adds a reading time property to the frontmatter of your Markdown or MDX files. Use this property to display the reading time for each page.

Recipe

<Steps> 1. Install Helper Packages
Install these two helper packages:
- [`reading-time`](https://www.npmjs.com/package/reading-time) to calculate minutes read
- [`mdast-util-to-string`](https://www.npmjs.com/package/mdast-util-to-string) to extract all text from your markdown

<PackageManagerTabs>
  <Fragment slot="npm">
  ```shell
npm install reading-time mdast-util-to-string
  ```
  </Fragment>
  <Fragment slot="pnpm">
  ```shell
pnpm add reading-time mdast-util-to-string
  ```
  </Fragment>
  <Fragment slot="yarn">
  ```shell
yarn add reading-time mdast-util-to-string
  ```
  </Fragment>
</PackageManagerTabs>

2. Create a remark plugin.

This plugin uses the `mdast-util-to-string` package to get the Markdown file's text. This text is then passed to the `reading-time` package to calculate the reading time in minutes. 

```js title="remark-reading-time.mjs"
import getReadingTime from 'reading-time';
import { toString } from 'mdast-util-to-string';

export function remarkReadingTime() {
  return function (tree, { data }) {
    const textOnPage = toString(tree);
    const readingTime = getReadingTime(textOnPage);
    // readingTime.text will give us minutes read as a friendly string,
    // i.e. "3 min read"
    data.astro.frontmatter.minutesRead = readingTime.text;
  };
}
```

3. Add the plugin to your config:

```js title="astro.config.mjs" "import { remarkReadingTime } from './remark-reading-time.mjs';" "remarkPlugins: [remarkReadingTime],"
import { defineConfig } from 'astro/config';
import { remarkReadingTime } from './remark-reading-time.mjs';

export default defineConfig({
  markdown: {
    remarkPlugins: [remarkReadingTime],
  },
});
```

Now all Markdown documents will have a calculated `minutesRead` property in their frontmatter. 

4. Display Reading Time

If your blog posts are stored in a [content collection](/en/guides/content-collections/), access the `remarkPluginFrontmatter` from the `render(entry)` function. Then, render `minutesRead` in your template wherever you would like it to appear.

```astro title="src/pages/posts/[slug].astro" "const { Content, remarkPluginFrontmatter } = await render(entry);" "<p>{remarkPluginFrontmatter.minutesRead}</p>"
---
import { getCollection, render } from 'astro:content';

export async function getStaticPaths() {
  const blog = await getCollection('blog');
  return blog.map(entry => ({
    params: { slug: entry.id },
    props: { entry },
  }));
}

const { entry } = Astro.props;
const { Content, remarkPluginFrontmatter } = await render(entry);
---

<html>
  <head>...</head>
  <body>
    ...
    <p>{remarkPluginFrontmatter.minutesRead}</p>
    ...
  </body>
</html>
```

If you're using a [Markdown layout](/en/basics/layouts/#markdown-layouts), use the `minutesRead` frontmatter property from `Astro.props` in your layout template.

```astro title="src/layouts/BlogLayout.astro" "const { minutesRead } = Astro.props.frontmatter;" "<p>{minutesRead}</p>"
---
const { minutesRead } = Astro.props.frontmatter;
---

<html>
  <head>...</head>
  <body>
    <p>{minutesRead}</p>
    <slot />
  </body>
</html>
```
</Steps>