SharePoint Framework (SPFx)では標準機能との見た目の統一感を考えてOffice UI Fabric Reactを使うことが多いのですが、個人的にはもっとオシャレにしたいとか楽に開発したいとかの理由でMaterial UIも割と利用しています。つい先日Material UI関連でハマってしまったのでその備忘と、ついでに導入方法も書いておきます。Reactを前提としています。
SPFxにMaterial UIを導入する方法
プロジェクト作成
まずはSPFxのプロジェクトを作ります。はじめての方はこちら。
マニフェストとかフォルダ構成とかはお好み次第で変えてください。
ベストプラクティスをまとめていますので参考にどうぞ。
Material UIをインストール
SPFx以外で利用する時と一緒です。npmで持ってきます。
npm i @material-ui/core @material-ui/icons typeface-roboto
テーマ作成
Material UIのテーマを作り、それを全体に展開するためにwithRoot関数を作成します。テーマなんか要らないよっていう人は飛ばしてください。
配置先はどこでも良いのですが、私はsrcフォルダ直下にwithRoot.tsxファイルを作成しています。コードはこんな感じ。
import * as React from 'react';
import { MuiThemeProvider, createMuiTheme, Theme,
StylesProvider, createGenerateClassName } from '@material-ui/core/styles';
import primaryColor from '@material-ui/core/colors/indigo';
import secondaryColor from '@material-ui/core/colors/grey';
import CssBaseline from '@material-ui/core/CssBaseline';
/** アプリケーション全体に適用するテーマ */
function theme() : Theme {
return createMuiTheme({
palette : {
primary : primaryColor,
secondary : secondaryColor,
text: {
primary : '#2c2c2c',
secondary : '#6c6c6c'
}
},
typography: {
fontSize: 11,
fontFamily: 'inherit',
h1: {
fontSize: 18
},
h2: {
fontSize: 16
}
}
});
}
/** テーマとスタイルを適用したコンポーネントを返却 */
function withRoot<P>(Component: React.ComponentType<P>) {
function WithRoot(props: P) {
const generateClassName = createGenerateClassName({
productionPrefix: 'myWebPart_'
});
return (
<MuiThemeProvider theme={theme()}>
<CssBaseline />
<StylesProvider generateClassName={generateClassName} >
<Component {...props} />
</StylesProvider>
</MuiThemeProvider>
);
}
return WithRoot;
}
/** テーマとスタイルを適用したコンポーネントを返却 */
export default withRoot;
コンポーネントにMaterial UIを導入
Webパーツを開発中であれば、React.Componentを継承するクラス(コンポーネント)が既定で1つありますのでそこにMaterial UIを盛り込みます。アプリケーションカスタマイザーなど既定でコンポーネントが無い場合は新しく作ります。以下サンプルです。
import * as React from 'react';
import msTheme from './MaterialUiSample.module.scss';
import styles from './MaterialUiSampleStyles';
import withRoot from '../../../withRoot';
import { Typography } from '@material-ui/core';
import withStyles, { WithStyles } from '@material-ui/core/styles/withStyles';
/** プロパティ定義 */
export interface IProps extends WithStyles<typeof styles> {
}
/** Material UI サンプル Webパーツ */
class MaterialUiSample extends React.Component<IProps, {}> {
public render(): React.ReactElement<IProps> {
return (
<div className={this.props.classes.Sample}>
<Typography>これはサンプルです。</Typography>
</div>
);
}
}
/** テーマとスタイルをプロパティに含めて返却 */
export default withRoot(withStyles(styles, { withTheme: true })(MaterialUiSample));
ここで読み込んでいる「MaterialUiSampleStyles」はスタイルを記述するためのファイルです。(私の好みでファイルを分けています。)こんな感じで作ります。
import { Theme } from '@material-ui/core/styles/createMuiTheme';
import createStyles from '@material-ui/core/styles/createStyles';
const styles = (theme: Theme) =>
createStyles({
Sample: {
color: 'red'
}
});
export default styles;
これでフォルダ構成はこうなりました。

srcフォルダ配下の構成
コンポーネントを呼び出す
WebパーツならBaseClientSideWebPartを、アプリケーションカスタマイザーならBaseApplicationCustomizerをそれぞれ継承するクラスでコンポーネントを呼び出すようにします。以下、Webパーツ開発時のサンプルです。
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import { IPropertyPaneConfiguration } from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import * as strings from 'MaterialUiSampleWebPartStrings';
import MaterialUiSample from './components/MaterialUiSample';
/** マニフェストで定義したプロパティの型定義 */
export interface IProps {
description: string;
}
/** Material UI サンプル Webパーツ */
export default class MaterialUiSampleWebPart
extends BaseClientSideWebPart <IProps> {
/** レンダリング */
public render(): void {
const element: React.ReactElement = React.createElement(
MaterialUiSample,
{
}
);
ReactDom.render(element, this.domElement);
}
/** 破棄イベント */
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
/** バージョン取得 */
protected get dataVersion(): Version {
return Version.parse('1.0');
}
/** プロパティウィンドウ定義 */
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: []
};
}
}
以上です。gulp serveして動作を確認してみてください。

サンプルコードの実行結果
SPFxにMaterial UIを導入する際の注意点
他のSPFxパッケージとのクラス名被り
上記サンプルで行っているようにproductionPrefixを指定しておくのが無難です。こうしておかないとSharePointサイトにMaterial UIを使う複数のアプリをインストールした時にcreateStylesで作成したクラスの名前が被ります。(productionPrefixを指定しないと「jss + 連番」がクラス名になるため)
以上、参考になれば幸いです。