Skip to main content
Date

Deploying Remix app on Vercel with mdx-bundler

インストール

Remix

npx create-remix@latest

app の種類は Just the basics、deploy 先は Vercel を選びます。

mdx-bundler

npm i mdx-bundler esbuild@0.12.9

esbuild を version 0.12.9 以下でインストールしてください。 Error: The package "esbuild-linux-64" could not be found, and is needed by esbuild.

ファイルを追加

app/content/post.mdx:

---
title: Example Post
published: 2021-02-13
description: This is some description
---

# Wahoo

import Demo from "./demo";

Here's a **neat** demo:

<Demo />

app/util/fs.server.ts:

import fs from "fs/promises";

const CONTENT = `${__dirname}/../app/content`;

export const readContentDir = async () => fs.readdir(CONTENT);

export const readContentFile = async (file: string) =>
  fs.readFile(`${CONTENT}/${file}`, "utf-8");

app/util/mdx.server.ts:

export { bundleMDX } from "mdx-bundler";

直接インポートするとクライアントバンドルに入れられてしまうので、専用ファイルを作り除外します。 Error: Could not resolve node:fs

app/routes/index.tsx:

import type { LoaderFunction } from "@remix-run/node";
import { json } from "@remix-run/node";
import { Link, useLoaderData } from "@remix-run/react";
import { readContentDir } from "~/util/fs.server";

export const loader: LoaderFunction = async () => {
  const files = await readContentDir();
  return json(files);
};

export default function Index() {
  const files = useLoaderData();
  return (
    <div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
      <h1>Welcome to Remix</h1>
      <ul>
        {files.map((file: string) => (
          <li key={file.replace(/\.mdx$/, "")}>
            <Link to={"/" + file.replace(/\.mdx$/, "")}>{file}</Link>
          </li>
        ))}
      </ul>
    </div>
  );
}

app/routes/$slug.tsx:

import * as React from "react";
import type { LoaderFunction } from "@remix-run/node";
import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { getMDXComponent } from "mdx-bundler/client";
import { bundleMDX } from "~/util/mdx.server";
import { readContentFile } from "~/util/fs.server";

export const loader: LoaderFunction = async ({ params }) => {
  const post = params.slug;
  if (!post) {
    throw new Response("Not Found", { status: 404 });
  }
  const source = await readContentFile(`${post}.mdx`);
  const data = await bundleMDX({
    source,
    files: {
      "./demo.tsx": `
import * as React from 'react'
function Demo() {
  return <div>Neat demo!</div>
}
export default Demo
`,
    },
  });
  return json(data);
};

export default function Post() {
  const { code, frontmatter } = useLoaderData();
  const Component = React.useMemo(() => getMDXComponent(code), [code]);
  return (
    <>
      <header>
        <h1>{frontmatter.title}</h1>
        <p>{frontmatter.description}</p>
      </header>
      <main>
        <Component />
      </main>
    </>
  );
}

remix.config.js:

module.exports = {
  serverBuildTarget: "vercel",
  server: process.env.NODE_ENV === "development" ? undefined : "./server.js",
  ignoredRouteFiles: ["**/.*"],
  serverDependenciesToBundle: [
    // ここにunified等、使用するESMパッケージを
  ],
};

Remix Gochas

コード

リンクは予告なく変更、削除する場合があります。

参考