diff --git a/public/logos/hackmd.svg b/public/logos/hackmd.svg new file mode 100644 index 0000000000000..7cf83b6e9b8ea --- /dev/null +++ b/public/logos/hackmd.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/content/docs/en/guides/cms/hackmd.mdx b/src/content/docs/en/guides/cms/hackmd.mdx new file mode 100644 index 0000000000000..c7c51e617622c --- /dev/null +++ b/src/content/docs/en/guides/cms/hackmd.mdx @@ -0,0 +1,218 @@ +--- +title: HackMD & Astro +description: Add content to your Astro project using HackMD as a CMS +sidebar: + label: HackMD +type: cms +stub: false +logo: hackmd +i18nReady: true +--- + +import { FileTree } from '@astrojs/starlight/components'; +import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'; +import ReadMore from '~/components/ReadMore.astro'; + +[HackMD](https://hackmd.io/) is a collaborative Markdown editor and publishing platform. You can use its API to manage your content in HackMD and display it in your Astro project. + +## Integrating with Astro + +This guide uses the official [`@hackmd/api`](https://github.com/hackmdio/api-client) client to fetch your notes and [`markdown-it`](https://github.com/markdown-it/markdown-it) to render Markdown content. + +### Prerequisites + +To get started, you will need: + +1. **An Astro project** - If you don't have an Astro project yet, the [installation guide](/en/install-and-setup/) will get you up and running. +2. **A HackMD account** - You can [sign up for free](https://hackmd.io/join). +3. **A HackMD access token** - Create one from the API section of your [HackMD settings](https://hackmd.io/settings#api). +4. **At least one publicly readable note** - Set the note's read permission to **Everyone** so the example can safely publish it on your site. + +### Setting up credentials + +Create a `.env` file in the root of your project and add your HackMD access token: + +```ini title=".env" +HACKMD_API_ACCESS_TOKEN= +``` + +Do not prefix this variable with `PUBLIC_`. This keeps the token available only to your server-side code and prevents Astro from exposing it to the browser. + +Read more about [environment variables](/en/guides/environment-variables/) and `.env` files in Astro. + +### Installing dependencies + +Install the HackMD API client and Markdown renderer: + + + + ```shell + npm install @hackmd/api markdown-it + ``` + + + ```shell + pnpm add @hackmd/api markdown-it + ``` + + + ```shell + yarn add @hackmd/api markdown-it + ``` + + + +### Configuring HackMD + +Create a `hackmd.ts` file in a new `src/lib/` directory. This file initializes the API client, renders Markdown, and creates a URL-friendly identifier for each note: + +```ts title="src/lib/hackmd.ts" +import { API } from '@hackmd/api'; +import MarkdownIt from 'markdown-it'; + +export const client = new API(import.meta.env.HACKMD_API_ACCESS_TOKEN); + +const md = new MarkdownIt({ + html: false, + linkify: true, + typographer: true, +}); + +export function renderMarkdown(content: string) { + return md.render(content); +} + +export function getNoteSlug(note: { permalink: string | null; shortId: string }) { + return note.permalink ?? note.shortId; +} +``` + +The `html: false` option prevents raw HTML in a note from being passed directly to your generated page. Standard Markdown is still rendered as HTML. + +Your project will use the following files: + + +- src/ + - lib/ + - **hackmd.ts** + - pages/ + - **index.astro** + - notes/ + - **[slug].astro** +- **.env** +- astro.config.mjs +- package.json + + +## Making a blog with Astro and HackMD + +This example creates an index of publicly readable notes and a statically generated page for each note. + +### Displaying a list of notes + +Use `getNoteList()` in `src/pages/index.astro` to retrieve your notes. Filter the results so that only notes with the `guest` read permission are included in the public site: + +```astro title="src/pages/index.astro" +--- +import { client, getNoteSlug } from '../lib/hackmd'; + +const notes = await client.getNoteList(); +const publicNotes = notes.filter((note) => note.readPermission === 'guest'); +--- + + + + + + + Astro + HackMD + + +
+

My HackMD notes

+ +
+ + +``` + +:::caution +The access token can read private notes in your account. Keep the `guest` permission filter unless you intentionally want to include other notes in the generated site. +::: + +### Generating note pages + +Create `src/pages/notes/[slug].astro` to generate a static page for every public note. The note list provides the route and note ID, then `getNote()` retrieves the full Markdown content for that page: + +```astro title="src/pages/notes/[slug].astro" +--- +import { client, getNoteSlug, renderMarkdown } from '../../lib/hackmd'; + +export async function getStaticPaths() { + const notes = await client.getNoteList(); + + return notes + .filter((note) => note.readPermission === 'guest') + .map((note) => ({ + params: { slug: getNoteSlug(note) }, + props: { noteId: note.id }, + })); +} + +interface Props { + noteId: string; +} + +const { noteId } = Astro.props; +const note = await client.getNote(noteId); +const content = renderMarkdown(note.content); +--- + + + + + + + {note.title} + + +
+
+ +
+
+ + +``` + +:::caution +Astro's [`set:html` directive](/en/reference/directives-reference/#sethtml) inserts an HTML string without escaping it. This example first passes the note through `markdown-it` with raw HTML disabled. If you enable the `html` option for trusted authors, sanitize the rendered result before passing it to `set:html`. +::: + +### Supporting more HackMD syntax + +HackMD uses `markdown-it` with extensions for features such as task lists, footnotes, containers, and a table of contents. The minimal configuration above handles standard Markdown. Install only the [`markdown-it` plugins](https://www.npmjs.com/search?q=keywords%3Amarkdown-it-plugin) required by your notes. + +### Publishing your site + +To deploy your website, visit our [deployment guides](/en/guides/deploy/) and follow the instructions for your preferred hosting provider. + +If your project uses Astro's default static mode, you must run a new build to publish changes made in HackMD. If your hosting provider supports it, you can use its webhook function to automatically trigger a new build when HackMD sends a [webhook event](https://hackmd.io/@docs/webhooks-events). + +## Official Resources + +- [HackMD API documentation](https://hackmd.io/@docs/developer-portal) +- [HackMD OpenAPI documentation](https://api.hackmd.io/v1/docs) + +## Community Resources + +- [`daily-oops`](https://github.com/Yukaii/daily-oops) - A blog that uses HackMD as its CMS +- [`astro-hackmd`](https://github.com/EastSun5566/astro-hackmd) - A minimal Astro site that uses HackMD as its CMS diff --git a/src/data/logos.ts b/src/data/logos.ts index 0013b6de63a37..be50539c7e9e6 100644 --- a/src/data/logos.ts +++ b/src/data/logos.ts @@ -59,6 +59,7 @@ export const logos = LogoCheck({ gitlab: { file: 'gitlab.svg' }, 'google-cloud': { file: 'google-cloud.svg', padding: '.1875em' }, gridsome: { file: 'gridsome.svg', padding: '.15em' }, + hackmd: { file: 'hackmd.svg', padding: '0' }, hashnode: { file: 'hashnode.png', padding: '.1875em' }, heroku: { file: 'heroku.svg', padding: '.25em' }, hostinger: { file: 'hostinger.svg', padding: '.2em' },