Astro React with MDX
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>
</>
)
}
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!