<-- mermaid -->

EmotionでスタイリングしたReactをWeb Componentとして利用する

あいさつ

こんにちは。Product Team の冨田、阿波連、渡邉、鈴木、長田です。*1
本記事では、EmotionでスタイリングしたReactコンポーネントをWeb Componentとして出力しようとした時に、困ったことがあったので、解決方法をお教えします。

私たちの困りごと

私たちは、ReactコンポーネントをEmotionでスタイリングして、それをWeb Componentsとして出力することにしました。
以下のような感じです。

import { createRoot } from "react-dom/client";
import { css } from "@emotion/react";

const App = () => {
  return (
    <div css={css`
      font-size: 32px;
    `}>
      hello world
    </div>
  );
}

class EmotionEl extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: "open" });
  }

  connectedCallback() {
    const root = createRoot(this.shadowRoot!);
    root.render(<App/>);
  }
}

customElements.define("emotion-el", EmotionEl);

Emotionを使ってスタイリングをして、カスタムエレメントとして利用可能にしています。
この <emotion-el> をカスタムエレメントとして利用するとどうなるかというと、スタイルが当たらず、ただただ hello world が表示されるだけでした。

原因はなんだろう?

ReactコンポーネントにEmotionで通常通りCSSを書くと、 <style><head> に入ってしまいます。
そのため、Shadow Rootの中にある要素 <emotion-el> へスタイリングが適用されなかったのです。

解決策

Shadow Rootの中に <style> が配置されればスタイリングが適用されますので、Emotionの機能で指定したところに <style> を配置するように指定できればよさそうです。

それには、Emotionのcache機能を使用するのが良さそうです。createCacheを使うことで、スタイルがどのように挿入されるかを指定できます。

この機能は次のようにして利用できます。CacheProviderと組み合わせることで、デフォルトのキャッシュをオーバーライドしてShadow Rootの中にある要素内に <style> を配置します。

import { createRoot } from "react-dom/client";
import { css, CacheProvider } from "@emotion/react";
import createCache from "@emotion/cache";

const App = () => {
  return (
    <div css={css`
      font-size: 32px;
    `}>
      hello world
    </div>
  );
}

class EmotionEl extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: "open" });
  }

  connectedCallback() {
    // container にShadow Domを指定することで、Shadow Domに <style> を配置してもらえるように指示する
    const cache = createCache({ key: "hoge", container: this.shadowRoot! });
    const root = createRoot(this.shadowRoot!);
    root.render(
      // CacheProviderでReactコンポーネントを囲む
      <CacheProvider value={cache}>
        <App/>
      </CacheProvider>
    );
  }
}

customElements.define("emotion-el", EmotionEl);

無事、Shadow Domにstyleを当てることができました🎉

この修正のよいところは、Reactコンポーネント(今回の例で言うとApp)には一切手を加えていないことです。

おわりに

今回はEmotionでスタイリングしたReactをWeb Componentとして利用する方法についてお話しました。 Reactのスタイルを当てる方法にはいくつかの種類があるので今後試していけたらと考えております。

ユーザベースでは一緒に働くメンバーを募集しています! マイクロフロントエンドやWeb Componentなど積極的に取り入れています。 すこしでも興味をお持ちになられた方がいましたら、カジュアルにお話からできますので、是非応募ください。お待ちしております。

https://hrmos.co/pages/uzabase/jobs?category=1766002544481845248

*1:本記事は、5名のエンジニアでモブをしながら作成しました

Page top