title | description | i18nReady | type |
---|---|---|---|
Add reading time |
Build a remark plugin to add reading time to your Markdown or MDX files. |
true |
recipe |
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.
1. Install Helper PackagesInstall 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>
-
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 thereading-time
package to calculate the reading time in minutes.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; }; }
-
Add the plugin to your config:
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. -
Display Reading Time
If your blog posts are stored in a content collection, access the
remarkPluginFrontmatter
from theentry.render()
function. Then, renderminutesRead
in your template wherever you would like it to appear.--- import { CollectionEntry, getCollection } from 'astro:content'; export async function getStaticPaths() { const blog = await getCollection('blog'); return blog.map(entry => ({ params: { slug: entry.slug }, props: { entry }, })); } const { entry } = Astro.props; const { Content, remarkPluginFrontmatter } = await entry.render(); --- <html> <head>...</head> <body> ... <p>{remarkPluginFrontmatter.minutesRead}</p> ... </body> </html>
If you're using a Markdown layout, use the
minutesRead
frontmatter property fromAstro.props
in your layout template.--- const { minutesRead } = Astro.props.frontmatter; --- <html> <head>...</head> <body> <p>{minutesRead}</p> <slot /> </body> </html>