Skip to main content

Astro React with MDX

TST, Hongkong

Astro App

Start the project using the Astro CLI and scaffold the Blog Template using strict Typescript:

npm create astro@latest

create-astro@4.7.4

astro Launch sequence initiated.

dir Where should we create your new project?
./astro-mdx

tmpl How would you like to start your new project?
Use blog template

ts Do you plan to write TypeScript?
Yes

use How strict should TypeScript be?
Strict

deps Install dependencies?
Yes

git Initialize a new git repository?

Verify that the dev server is operational by running:

npm run dev

astro v4.5.5 ready in 261 ms

┃ Local http://localhost:4321/
┃ Network use --host to expose

And visiting the created blog template under:

http://localhost:4321/

Now stop the server and add the MDX and React support using the command below:

npx astro add mdx
npx astro add react
npm install react react-dom @types/react @types/react-dom

Make sure that Astro is aware of the React and MDX dependencies:

astro.config.mjs

import { defineConfig } from 'astro/config'
import mdx from '@astrojs/mdx'
import react from '@astrojs/react'
import sitemap from '@astrojs/sitemap'

import react from "@astrojs/react"

// https://astro.build/config
export default defineConfig({
site: 'https://example.com',
integrations: [
mdx(),
sitemap(),
react({
include: ['**/react/*'],
})
]
});

By choosing the Blog Template the application already has a few template markdown articles under src/content/blog. There is also an MDX file already available src/content/blog/using-mdx.mdx - and I am not sure if this was already part of the Blog Template - adding MDX support may have been redundant (?).

But let's try mixing some React with Markdown anyway and see what happens:

src/content/blog/mdx-test.mdx:

---
title: 'Countries by GDP'
description: 'Top 10 of countries by their Gross Domestic Products (GDP).'
pubDate: 'Mar 18 2024'
heroImage: '/blog-placeholder-about.jpg'
---


import {CountryList} from '../../components/react/countries.tsx'


<CountryList />

<div>
> <code><small>Source [ Gross domestic product 2022 - World Bank](https://databankfiles.worldbank.org/public/ddpext_download/GDP.pdf)</small></code>
</div>

This MDX file follows the structure (frontmatter) of the example files, making sure that it will be picked up by the markdown template as page content and placed in the list of available blog entries.

The React part is not defined directly inside the MDX file but inmported from a TSX component that simulates retrieving the GDP data from an API, sorts the entries by GDP and displays them in an unordered list:

src/components/react/countries.tsx:

const countryList: { id: string, name: string, gdp: string }[] = [
{
"id": "UK",
"name": "United Kingdom",
"gdp": "3070668"
}, {
"id": "RUS",
"name": "Russian Federation",
"gdp": "2240422"
}, {
"id": "IT",
"name": "Italy",
"gdp": "2010432"
}, {
"id": "CN",
"name": "China",
"gdp": "17963171"
}, {
"id": "FR",
"name": "France",
"gdp": "2782905"
}, {
"id": "JP",
"name": "Japan",
"gdp": "4231141"
}, {
"id": "GER",
"name": "Germany",
"gdp": "4072192"
}, {
"id": "CA",
"name": "Canada",
"gdp": "2139840"
}, {
"id": "IN",
"name": "India",
"gdp": "3385090"
}, {
"id": "US",
"name": "United States",
"gdp": "25462700"
}
]

export function CountryList(): JSX.Element {

const renderList = (countries: { id: string, name: string, gdp: string }[]) => {
countries.sort((a, b) => parseFloat(b.gdp) - parseFloat(a.gdp))
return countries.map(country => <li>{country.name}: {(parseFloat(country.gdp) / 1000000).toFixed(2)}</li>)
}

return (
<>
<h5>Countries by GDP (in Million $)</h5>
<ul>
{renderList(countryList)}
</ul>
</>
)
}

Astro React with MDX

Running a build:

npm run build

> astro-mdx@0.0.1 build
> astro check && astro build

20:11:28 [vite] Re-optimizing dependencies because vite config has changed
20:11:28 Types generated 395ms
Result (16 files):
- 0 errors
- 0 warnings
- 0 hints

20:11:39 [build] output: "static"
20:11:39 [build] directory: astro-mdx/dist/
20:11:39 [build] Collecting build info...
20:11:39 [build] ✓ Completed in 458ms.
20:11:39 [build] Building static entrypoints...
20:11:41 [vite] ✓ built in 2.45s
20:11:41 [build] ✓ Completed in 2.60s.

building client (vite)
20:11:42 [vite] ✓ 23 modules transformed.
20:11:42 [vite] dist/_astro/client.CwWKiGVO.js 142.28 kB │ gzip: 45.84 kB
20:11:42 [vite] ✓ built in 697ms

generating static routes
20:11:42 ▶ src/pages/about.astro
20:11:42 └─ /about/index.html (+16ms)
20:11:42 ▶ src/pages/blog/index.astro
20:11:42 └─ /blog/index.html (+6ms)
20:11:42 ▶ src/pages/blog/[...slug].astro
20:11:42 ├─ /blog/markdown-style-guide/index.html (+8ms)
20:11:42 ├─ /blog/using-mdx/index.html (+15ms)
20:11:42 ├─ /blog/first-post/index.html (+7ms)
20:11:42 ├─ /blog/second-post/index.html (+8ms)
20:11:42 ├─ /blog/third-post/index.html (+5ms)
20:11:43 └─ /blog/mdx-test/index.html (+10ms)
20:11:43 λ src/pages/rss.xml.js
20:11:43 └─ /rss.xml (+5ms)
20:11:43 ▶ src/pages/index.astro
20:11:43 └─ /index.html (+3ms)
20:11:43 ✓ Completed in 334ms.

20:11:43 [@astrojs/sitemap] `sitemap-index.xml` created at `dist`
20:11:43 [build] 9 page(s) built in 4.12s
20:11:43 [build] Complete!