人に話すとたまに驚かれますが、じつはNext.jsの中でPython環境を動かすことができます。
Pyodideという、WASMのおかげでブラウザ上で使えるPython環境があるのですが、それをReactの中で使いやすくなるhookなどが提供されるreact-pyというnpmパッケージも存在します。
前回の記事と同じように、クライアント側でしか動かない仕組みですが、今回はNext.js 13のクライアント コンポーネントという機能を使ってインポートなどをわかりやすくしようと思いました。
今日の流れはこのようになります:
- Next.js 13(TypeScript)のテンプレートから始める
-
react-py
でPythonコードが入力・実行できるコンポーネントを用意 - Chakraを使って簡単に見た目を良くする
Next.js 13(TypeScript)のテンプレートから始める
現在、デフォルトでNext.js 13とTypeScriptが導入されます。
yarn create next-app
npm
やpnpm
、別のツールを使うことももちろん可能です。この記事ではyarn
で進めていきます。
また、色々と選択することになると思いますが、デフォルトと違った自分の選択肢を共有します:
- Would you like to use Tailwind CSS with this project? › No
Chakraを使う予定でしたので、Tailwindは不要だと思いました(Python環境の部分に影響ありません) - Would you like to customize the default import alias? › Yes
What import alias would you like configured? … @/*
プロジェクト内のコンポーネントがインポートしやすくなります(Python環境の部分に影響ありません)
react-py
でPythonコードが入力・実行できるコンポーネントを用意
まずはreact-py
をインストールしましょう。
yarn add react-py
ドキュメントではもはやNext.js 13のクライアント コンポーネントでの使い方しか紹介されませんが、Next.js 12ではnext/dynamic
でインポートできます。既存のプロジェクトでreact-pyをそのまま使いたい場合はNext.js + p5.jsについて書いた記事を参照してください。やり方は同じになります。
今回はあえて最新のNext.js 13で使える、より簡単な方法を紹介したいと思います。先ほどドキュメントへのリンクを貼りましたが、まずはPython環境を使いたいコンポーネントかページの一行目に'use client'
を付けてクライアント コンポーネントにしましょう。クライアント コンポーネントはサーバー側でのNext.jsによる生成の対象外になるのでnext/dynamic
などが不要になります。
PythonProviderを挿入する
Pythonようにクライアント コンポーネントにしたいページかコンポーネントの一番上をこのようにしましょう:
'use client'
import { PythonProvider } from 'react-py'
そのPythonProvider
を本ページの出力に加えましょう。react-py
のhookを使うことになるコンポーネントを囲うようにしましょう:
export default function Home() {
return (
<PythonProvider>
<main>
<Codeblock />
</main>
</PythonProvider>
)
}
Codeblock
はこのチュートリアルの任意コンポーネントになります。
Codeblockコンポーネント
react-py
の使い道はいっぱいあると思いますが、この度はドキュメントの通り<textarea>
にPythonコードを書いて実行できるコンポーネントにしましょう。
まずはusePython
hookをインポートしましょう。クライアント コンポーネントにしますので、先ほどのPythonProvider
と同じくそのままインポートできます:
'use client'
import { usePython } from 'react-py'
コンポーネントの定義の中でusePython
を使いましょう:
export default function Codeblock() {
const { runPython, stdout, stderr, isLoading, isRunning } = usePython()
return (
<>
...
</>
)
}
取得するのは:
-
runPython: (code: string) => Promise<void>
: Pythonコードを渡すと実行してくれる関数 -
stdout: string
: Pythonの標準出力 -
stderr: string
: Pythonの標準エラー出力 -
isLoading: boolean
: Python環境を読み込み中かどうか(WASM自体がcdn.jsdelivr.netから読み込まれます) -
isRunning: boolean
: Python環境が処理中かどうか
usePython
からisReady
など取得できる定数や関数が他にもありますので、必要に応じて使いたいと思います。
Chakraを使って簡単に見た目を良くする
最後に、本題ではないですがChakra UIで形にしたいと思います。Chakraを初めてNext.js 13で使ってみましたので勉強になったので、かるく共有したいと思います。
Pyodideと直接関係がないので、違うウェブコンポーネントを使う場合は下記読んでいただかなくても大丈夫だと思います。一応形にしなければならいないと思いましたので、割と簡単な方法で対応してみたまでです。
まずはChakraなどをインストールしましょう。
yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion
次に、react-py
にPythonProvider
があるようにChakraにChakraProvider
というのがあります。Chakraコンポーネントを使うところをこれで囲いたいです。
Next.js 13のlayout.tsx
をこのようにしました:
import { ChakraProvider } from '@/tools/chakra-client-components'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<head>
<title>Next.js 13 + Chakra + react-py</title>
</head>
<body>
<ChakraProvider>{children}</ChakraProvider>
</body>
</html>
)
}
ChakraProvider
は、クライアント コンポーネントではなくても使うことができますので、RootLayoutをクライアントコンポーネントにする必要がありません。サーバー側生成とChakraはちゃんと同時に使えます。
あとはChakraのドキュメントと完成したコンポーネントのコードを参照してください:
一番注意したいところは、HTML5のtextarea
タグのdisabled
プロパティは、ChakraのTextarea
コンポーネントとなるとisDisabled: boolean
を使わなければならないところです。
最後に
Next.jsで外部APIを使わずにPythonをブラウザ上で実行できました。また、Next.js 13の新しいクライアント コンポーネントを使ってブラウザ側でしか使えないコンポーネントやhookをスムーズにインポートできました。
任意のPythonパッケージのインストールなどPyodideが充実していますので、したいことが意外と可能だったりしますのでユーザーの生データを外部APIに送りたくない場面では積極的に考えたい選択肢だと思います。
サンプルリポもよかったらご参照ください。そしてご不明点、ご要望などありましたらコメントやツイッターでご連絡いただければ嬉しいです。