I recently ported this site from Gatsby to Astro + MDX + Tailwind and can not be happier. Gatsby is Great but it’s more than I need here.
Astro is a tool that allows you to easily set up a static site with MDX and Tailwind CSS. MDX is a syntax that lets you mix JSX and Markdown in the same file, which makes it easy to write and style components that include both rich text and code. Tailwind CSS is a utility-based CSS framework that provides a set of low-level classes that you can use to style your components.
Using Astro with MDX and Tailwind can be a good choice for building static sites because it allows you to easily create complex, interactive components that include both rich text and code. It also gives you the flexibility to style your components using a variety of low-level classes, rather than having to write custom CSS for each component. This can save you time and effort when building your site, and make it easier to maintain and update in the future.
Preact is a lightweight JavaScript library that is designed to be a fast and lightweight alternative to React, with a similar API. It can be used in place of React in any application, including an application that uses MDX.
To use Preact in an application that uses MDX, you will need to install the Preact library and the @preact/compat package, which provides compatibility with the React API. You can then import the Preact components and functions that you need, just as you would in a regular React application.
For example, you might import the h function and the Component class from Preact, and use them to create a custom component that can be rendered in an MDX document:
import {
h,
Component
} from 'preact';
export class MyComponent extends Component {
render() {
return <div > Hello, Preact! </div>;
}
}
You can then use this component in an MDX document like this:
---
import {MyComponent} from './my-component';
---
<MyComponent / >
This will render the MyComponent component in the MDX document, using Preact to manage the component’s state and rendering.
Let’s set up a sample project and deploy it to GitLab pages. I assume you can create a GitLab project and us Git.
npm create astro@latest
Deploy to GitLab pages
mv public static
// astro.config.mjs
import {
defineConfig
} from 'astro/config';
export default defineConfig({
site: 'https://jameskolean.gitlab.io',
base: import.meta.env.MODE === 'development' ? '/' : '/astro-mdx-tailwind',
// GitLab Pages requires exposed files to be located in a folder called "public".
// So we're instructing Astro to put the static build output in a folder of that name.
outDir: 'public',
// The folder name Astro uses for static files (`public`) is already reserved
// for the build output. So in deviation from the defaults we're using a folder
// called `static` instead.
publicDir: 'static',
});
Note: Change
site
to your own site and changebase
to your own base.
# .gitlab-ci.yml
# This folder is cached between builds
# https://docs.gitlab.com/ce/ci/yaml/README.html#cache
cache:
paths:
- node_modules/
# Enables git-lab CI caching. Both .cache and public must be cached, otherwise builds will fail.
- .cache/
- public/
pages:
script:
- npm install
- npm run build
artifacts:
paths:
- public
only:
- main
All commands are run from the root of the project, from a terminal:
Command | Action |
---|---|
npm install | Installs dependencies |
npm run dev | Starts local dev server at localhost:3000 |
npm run build | Build your production site to ./public/ |
npm run preview | Preview your build locally, before deploying |
npm run astro ... | Run CLI commands like astro add , astro preview |
npm run astro --help | Get help using the Astro CLI |
npx astro add mdx
VS Code supports Markdown by default. However, for MDX editor support, you may wish to add the following setting in your VSCode config. This ensures authoring MDX files provides a Markdown-like editor experience.
"files.associations": {
"*.mdx": "markdown"
}
Now lets add some markdown files we can extend later
---
// src/layouts/LayoutMarkdown.astro
import Layout from "./Layout.astro";
export interface Props {
title: string;
}
const props = Astro.props;
---
<Layout title={props.title}>
<h2>Markdown Template</h2>
<p>Markdown content is below</p>
<slot />
</Layout>
---
# src/pages/md-page.md
layout: '../layouts/LayoutMarkdown.astro'
title: Markdown Example
---
# Markdown
This Markdown file creates a page at `your-domain.com/md-page/`
It probably isn't styled much, but Markdown does support:
* **bold** and _italics._
* lists
* [links](https://astro.build)
* and more!
---
# src/pages/mdx-page.mdx
layout: '../layouts/LayoutMarkdown.astro'
title: MDX Example
---
# MDX
This MDX file creates a page at `your-domain.com/mdx-page/`
Now Let’s add a Preact component
npx astro add preact
Add Compat
// astro.config.mjs
import {
defineConfig
} from 'astro/config';
import mdx from '@astrojs/mdx';
import preact from "@astrojs/preact";
const MODE =
import.meta.env.MODE;
// https://astro.build/config
export default defineConfig({
site: 'https://jameskolean.gitlab.io',
base: MODE === 'development' ? '/' : '/astro-mdx-tailwind',
// GitLab Pages requires exposed files to be located in a folder called "public".
// So we're instructing Astro to put the static build output in a folder of that name.
outDir: 'public',
// The folder name Astro uses for static files (`public`) is already reserved
// for the build output. So in deviation from the defaults we're using a folder
// called `static` instead.
publicDir: 'static',
markdown: {
drafts: true
},
integrations: [mdx({
drafts: true
}), preact({
compat: true
})]
});
// src/components/Counter.jsx
import { useState } from 'preact/hooks';
import "./Counter.css";
export default function Counter({ children }) {
const [count, setCount] = useState(0);
const add = () => setCount((i) => i + 1);
const subtract = () => setCount((i) => i - 1);
return (
<>
<div class="counter">
<button onClick={subtract}>-</button>
<pre>{count}</pre>
<button onClick={add}>+</button>
</div>
<div class="counter-message">{children}</div>
</>
);
}
/* src/components/Counter.css */
.counter {
display: grid;
font-size: 2em;
grid-template-columns: repeat(3, minmax(0, 1fr));
margin-top: 2em;
place-items: center;
}
.counter-message {
text-align: center;
}
---
layout: '../layouts/LayoutMarkdown.astro'
title: MDX Example
---
import Counter from '../components/Counter'
# MDX
This MDX file creates a page at `your-domain.com/mdx-page/`
<Counter client:idle>
<h1>Hello, Preact 1!</h1>
</Counter>
npx astro add tailwind
---
// src/layouts/LayoutMarkdown.astro
import Layout from "./Layout.astro";
export interface Props {
title: string;
}
const props = Astro.props;
---
<Layout title={props.title}>
<div class="mx-auto max-w-3xl">
<h2>Markdown Template</h2>
<p>Markdown content is below</p>
<div class="mt-5 max-w-prose bg-slate-300">
<slot />
</div>
</div>
</Layout>