前回、認証ページの基本的な画面は実装できたので、今回、認証ページに対してAmplify UI Componentsを使って修正を加えていく。

今回修正を加えるポイントは以下の3つ。
- 画面構成の変更(ログイン前ページの表示、共通メニューの表示など)
- 日本語対応
- Amplify UI Componentsを適用して体裁を整える
完成後のトップページは以下になる。

以前記載した通り、aws-amplify/ui-react をインストールしていることを前提に進める。
npm install aws-amplify @aws-amplify/ui-react
目次
4階:React/JSXで画面を作る<応用編>
画面構成の変更
以下の仕様とする。
- ログイン前にトップページにアクセスできる。
- ログイン前後でハンバーガーメニューの要素を切り替える。
- 会員登録時のフィールドは、メールアドレス、ニックネームのみ。
- ログイン後はニックネームの変更ができる。
- その際、アカウント設定表示→アカウント変更画面→変更、の流れとする。
以下の構成に変更する。

前回のようにトップページをログインページにするとログイン前ページを表示できないため、トップページはログイン前後で表示できるようにする。ログイン後はトップページを表示するようにする。
App.js
まずは大元のApp.jsを修正する。
前回のように各ページに <Authenticator.Provider> タグを入れるのは冗長になるため、大元のApp.jsで記述しておく。
また、「アカウント情報表示」と「アカウント情報更新」を分ける構成にする。
Layout は、各画面の共通要素であるタイトルとハンバーガーメニューを実装するために追加する。ネスト構造にすることで全画面に表示されるようになる。
■/src/App.js
import { Amplify } from 'aws-amplify';
import '@aws-amplify/ui-react/styles.css';
import { Route, Routes, BrowserRouter } from "react-router-dom";
import LoginPage from "./pages/LoginPage";
import UserEdit from "./pages/UserEdit";
import Layout from "./pages/Layout"; ←追加
import User from "./pages/User"; ←追加
import MyPage from "./pages/MyPage"; ←追加
import { Authenticator } from '@aws-amplify/ui-react'; ←追加
import awsExports from './aws-exports';
Amplify.configure(awsExports);
function App() {
return (
<Authenticator.Provider> ←追加
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />} > ←追加
<Route path="/" element={<MyPage />} /> ←変更
<Route path="/login" element={<LoginPage />} /> ←変更
<Route path="/user" element={<User />} /> ←追加
<Route path="/user_edit" element={<UserEdit />} />
</Route>
</Routes>
</BrowserRouter>
</Authenticator.Provider> ←追加
);
}
export default App;
LoginPage.js
サインアップ(会員登録)時の必要フィールドを「メールアドレス」と「ニックネーム」のみにとし、シンプルな構成にする。
前述の通り、<Autenticator.Provider>は不要なのでカットする。
ログイン後はMyPage(トップページ)に遷移させる。
■/src/pages/LoginPage.js
import { Authenticator } from '@aws-amplify/ui-react';
import MyPage from "./MyPage";
const LoginPage = () => {
return (
<Authenticator signUpAttributes={[
'email', ←追加
'nickname', ←追加
]}>
{({ signOut, user }) => (
<MyPage /> ←<Authenticator.Provider>をカット
)}
</Authenticator>
);
}
export default LoginPage;
MyPage.js
ログイン前・ログイン後にMyPage(トップページ)を表示する。
ログイン状態を取得sるため、context.route を取得し、”authenticated”でない(ログインしていない)場合は何も処理をせず、ログインしている場合のみ「ニックネーム」を表示する。
以下を参照した。
■Advanced Usage | Amplify UI for React
https://ui.docs.amplify.aws/react/connected-components/authenticator/advanced#access-auth-state
■How to create an application with protected routes using the Authenticator component | Amplify UI for React
https://ui.docs.amplify.aws/react/guides/auth-protected
■/src/pages/MyPage.js
import { useAuthenticator } from '@aws-amplify/ui-react';
const MyPage = () => {
const { user, signOut } = useAuthenticator((context) => [
context.user,
context.route,
]);
return (
<>
{
route !== 'authenticated' ?
''
:
<>
<h6>ようこそ、{user.attributes.nickname}さん</h6>
</>
}
</>
);
}
export default MyPage;
User.js
Login.jsと同様、UserPage.jsを呼び出すのみ。ページを増やすたびに必要になるので改善の余地があるが、今回はいったんこのまま進める。
■/src/pages/User.js
import { Authenticator } from '@aws-amplify/ui-react';
import UserPage from "./UserPage";
const User = () => {
return (
<Authenticator signUpAttributes={[
'email',
'nickname',
]}
>
{({ signOut, user }) => (
<UserPage />
)}
</Authenticator>
);
}
export default User;
UserPage.js
ユーザー情報を表示する画面。いったん仮組みしておく。
■/src/pages/UserPage.js
import { useAuthenticator } from '@aws-amplify/ui-react';
const UserPage = () => {
const { user } = useAuthenticator((context) => [context.user]);
return (
<>
<>
<h4>アカウント設定</h4>
</>
<>
ニックネーム:{user.attributes.nickname}
</>
<>
メールアドレス:{user.attributes.email}
</>
</>
);
}
export default UserPage;
UserEdit.js
UserEdit.jsも、Login.js、User.jsと同様の形となる。
■/src/pages/UserEdit.js
import { Authenticator } from '@aws-amplify/ui-react';
import UserEditPage from "./UserEditPage";
const UserEdit = () => {
return (
<Authenticator signUpAttributes={[
'email',
'nickname',
]}>
{({ signOut, user }) => (
<UserEditPage />
)}
</Authenticator>
);
}
export default UserEdit;
UserEditPage.js
UserEditPage.jsは前回のままとする。のちほど整形する。
■/src/pages/UserEditPage.js
import { useAuthenticator } from '@aws-amplify/ui-react';
import { useState } from "react";
import { Auth } from 'aws-amplify';
const UserEditPage = () => {
const { user, signOut } = useAuthenticator((context) => [context.user]);
const [nickname, setNickname] = useState(user.attributes.nickname);
console.log(nickname);
async function updateUserInfo() {
let result = null;
try {
const user = await Auth.currentAuthenticatedUser();
result = await Auth.updateUserAttributes(user, {
nickname: nickname
});
} catch(error) {
console.log('error update: ', error);
}
if (result === 'SUCCESS') {
alert("ユーザー情報を更新しました!");
}
else {
alert("ユーザー情報の更新に失敗しました・・・");
}
return result;
}
return (
<main>
<li>email: <input defaultValue={user.attributes.email} /></li>
<li>nickname: <input defaultValue={nickname} onChange={e => setNickname(e.target.value)} /></li>
<p><button onClick={() => updateUserInfo()}>ユーザー情報を変更する</button></p>
<p><button onClick={signOut}>ログアウトする</button></p>
</main>
);
}
export default UserEditPage;
Layout.js
Layout.jsは、共通のグローバルメニューになる。後ほど整理して実装するので現状は仮組みの状態である。
■/src/pages/Layout.js
import { Outlet } from 'react-router-dom';
import { useAuthenticator } from '@aws-amplify/ui-react';
import { Link } from '@aws-amplify/ui-react';
const Layout = () => {
const { route, signOut } = useAuthenticator((context) => [
context.route,
context.signOut,
]);
return (
<>
<>
<h3><Link href="/">Twin kangaroos</Link></h3>
</>
<>
{
route === 'authenticated' ?
<>
<button onClick={signOut}>ログアウト</button>
</>
:
<>
<button onClick={() => window.location.href = '/login'}>ログイン</button>
</>
}
</>
<>
{
route !== 'authenticated' ?
<>
ログインすると会員限定コンテンツを見ることができます。
</>
:
''
}
<Outlet />
</>
</>
);
}
export default Layout;
画面イメージ(途中経過)
画面イメージは以下になる。UIは後ほど手を加えていくので、この時点ではエラーが出ないことを確認しておく。



日本語対応
デフォルトでは全て英語で表示されるので日本語化を行う。以下を参考にして進める。
■Customization | Amplify UI for React
https://ui.docs.amplify.aws/react/connected-components/authenticator/customization#internationalization-i18n
■amplify-ui/en.ts at main · aws-amplify/amplify-ui · GitHub
https://github.com/aws-amplify/amplify-ui/blob/main/packages/ui/src/i18n/dictionaries/authenticator/en.ts
■/src/App.js
import { Amplify } from 'aws-amplify';
import '@aws-amplify/ui-react/styles.css';
import { Route, Routes, BrowserRouter } from "react-router-dom";
import LoginPage from "./pages/LoginPage";
import UserEdit from "./pages/UserEdit";
import Layout from "./pages/Layout"; ←追加
import User from "./pages/User"; ←追加
import MyPage from "./pages/MyPage"; ←追加
import { Authenticator, translations } from '@aws-amplify/ui-react'; ←変更
import awsExports from './aws-exports';
Amplify.configure(awsExports);
↓追加
I18n.putVocabularies(translations);
I18n.setLanguage('ja');
I18n.putVocabularies({
ja: {
'Sign in': 'ログインする',
'Sign In': 'ログイン',
'Sign Up': '会員登録する',
'Create Account': '会員登録',
'Email': 'メールアドレス',
'Enter your Email': 'メールアドレスを入力してください',
'Enter your Password': 'パスワードを入力してください',
'Password': 'パスワード',
'Please confirm your Password': '確認用パスワードを入力してください',
'Nickname': 'ニックネーム',
'Enter your Nickname': 'ニックネームを入力してください',
'Your passwords must match': 'パスワードを合致させてください',
'Invalid verification code provided, please try again.': '確認コードに誤りがあるため、再度お試しください',
'Back to Sign In': 'ログイン画面に戻る',
'Cannot reset password for the user as there is no registered/verified email or phone_number': '会員登録されていないためパスワードリセットできません',
},
});
↑追加
function App() {
return (
<Authenticator.Provider>
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />} >
<Route path="/" element={<MyPage />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/user" element={<User />} />
<Route path="/user_edit" element={<UserEdit />} />
</Route>
</Routes>
</BrowserRouter>
</Authenticator.Provider>
);
}
export default App;
ログイン画面は以下のように日本語化される。

会員登録ページは以下のようになる。

Amplify UI Componentsを適用
UIのカスタマイズを行うため、以下のAmplify UI のドキュメントを読みつつ進める。
■Components | Amplify UI for React
https://ui.docs.amplify.aws/react/components
LoginPage.js
ヘッダーフッターは、以下を参考にしながらカスタマイズする。
■Customization | Amplify UI for React
https://ui.docs.amplify.aws/react/connected-components/authenticator/customization#headers–footers
■/src/pages/LoginPage.js
import { Authenticator, useAuthenticator } from '@aws-amplify/ui-react'; ←useAuthenticator追加
import { useTheme, View, Text, Heading, Button } from '@aws-amplify/ui-react'; ←追加
import MyPage from "./MyPage";
↓追加
const components = {
// 共通フッター
Footer() {
const { tokens } = useTheme();
return (
<View textAlign="center" padding={tokens.space.large}>
<Text color={tokens.colors.neutral[80]}>
© Twin kangaroos All Rights Reserved
</Text>
</View>
);
},
// ログイン
// ━━━━━━━━━━
SignIn: {
// ログインのヘッダー
Header() {
const { tokens } = useTheme();
return (
<Heading
padding={`${tokens.space.xl} 0 0 ${tokens.space.xl}`}
level={4}
>
ログイン
</Heading>
);
},
// ログインのフッター
Footer() {
const { toResetPassword } = useAuthenticator();
return (
<View textAlign="center">
<Button
fontWeight="normal"
onClick={toResetPassword}
size="small"
variation="link"
>
パスワードを忘れた方
</Button>
</View>
);
},
},
// 会員登録
// ━━━━━━━━━━
SignUp: {
Header() {
const { tokens } = useTheme();
return (
<Heading
padding={`${tokens.space.xl} 0 0 ${tokens.space.xl}`}
level={4}
>
Twin kangaroosに会員登録
</Heading>
);
},
Footer() {
const { toSignIn } = useAuthenticator();
return (
<View textAlign="center">
<Button
fontWeight="normal"
onClick={toSignIn}
size="small"
variation="link"
>
ログイン画面に戻る
</Button>
</View>
);
},
},
// 会員登録画面→確認コードの確認
// ━━━━━━━━━━
ConfirmSignUp: {
Header() {
const { tokens } = useTheme();
return (
<Heading
padding={`${tokens.space.xl} 0 0 ${tokens.space.xl}`}
level={3}
>
確認コードの入力
</Heading>
);
},
Footer() {
return <Text></Text>;
},
},
};
↑追加
const LoginPage = () => {
return (
<Authenticator signUpAttributes={[
'email',
'nickname',
]} components={components} ←追加
>
{({ signOut, user }) => (
<MyPage />
)}
</Authenticator>
);
}
export default LoginPage;
MyPage.js
MyPage.jsでは、Tabs・TabItemコンポーネントを用いてタブでページ切り替えを可能とする。
■/src/pages/MyPage.js
import { useAuthenticator } from '@aws-amplify/ui-react';
import { Heading, Flex, Tabs, TabItem } from '@aws-amplify/ui-react';
import Home from "./Home";
const MyPage = () => {
const { user, signOut } = useAuthenticator((context) => [
context.user,
context.route,
]);
return (
<>
{
route !== 'authenticated' ?
''
:
<Flex direction="column" gap="1rem" alignItems="center">
<Heading level={6}>ようこそ、{user.attributes.nickname}さん</Heading>
</Flex>
}
<Tabs justifyContent="flex-start">
<TabItem title="Home">
<Home />
</TabItem>
<TabItem title="注目記事">
<Home />
</TabItem>
<TabItem title="トップ10">
<Home />
</TabItem>
<TabItem title="Premium" isDisabled={true}>
Cannot click
</TabItem>
</Tabs>
</>
);
}
export default MyPage;
Home.js
Home.jsでは、ダミーの記事を表示する。
■/src/pages/Home.js
import { View, Heading, Flex, Link } from '@aws-amplify/ui-react';
const Home = () => {
return (
<>
<View padding="1rem">
<Heading level={5} padding="0.5rem">React Amplifyの事例</Heading>
<Flex direction="column" padding="0.5rem">
React Amplifyは、AWSのバックエンドサービスをフロントエンドアプリケーションに統合するためのライブラリです。以下に、React Amplifyを使用して開発された成功事例をいくつか紹介します。
</Flex>
<Heading level={6} padding="0.5rem">1.Intuit QuickBooks</Heading>
<Flex direction="column" padding="0.5rem">
Intuit QuickBooksは、小規模企業向けの会計ソフトウェアです。React Amplifyを使用して、ユーザー認証、ファイルのアップロード、データの保存などの機能を実装しています。
</Flex>
<Heading level={6} padding="0.5rem">2.Comcast</Heading>
<Flex direction="column" padding="0.5rem">
Comcastは、米国のケーブルテレビ・インターネットサービスプロバイダーです。React Amplifyを使用して、Xfinityストリーミングサービスのサブスクリプション管理などの機能を実装しています。
</Flex>
<Heading level={6} padding="0.5rem">3.Philips Hue</Heading>
<Flex direction="column" padding="0.5rem">
Philips Hueは、LED照明を制御するIoT製品です。React Amplifyを使用して、ユーザー認証、デバイスの追加、シナリオの作成などの機能を実装しています。
</Flex>
</View>
</>
);
}
export default Home;
UserPage.js
アカウント設定画面を作る。「パスワード」「メールマガジンを受け取る」はダミーである。
■/src/pages/UserPage.js
import { useAuthenticator } from '@aws-amplify/ui-react';
import { Icon, View, Heading, Flex, Text, useTheme, Divider, Link, SwitchField } from '@aws-amplify/ui-react'; ←追加
const UserPage = () => {
const { user } = useAuthenticator((context) => [context.user]);
const { tokens } = useTheme(); ←追加
return (
<>
↓大幅に変更
<Flex direction="column" gap="1rem" alignItems="flex-start">
<Heading level={4}>アカウント設定</Heading>
</Flex>
<View
backgroundColor={tokens.colors.background.secondary}
padding={tokens.space.large}
>
<Flex direction="column">
<Flex direction="row">
<Link href="/">
<Icon
pathData="M15.41 7.41 14 6l-6 6 6 6 1.41-1.41L10.83 12l4.58-4.59Z"
viewBox={{
width: 20,
height: 26,
}}
width="20"
height="26"
color="gray"
ariaLabel="back"
/>
</Link>
<Heading level={5}>アカウント</Heading>
</Flex>
<Divider orientation="horizontal" />
<Flex direction="row">
<Flex direction="column" alignItems="flex-start" padding="1rem">
<Link href="/user_edit">
<Flex direction="row" alignItems="flex-end" padding="0.5rem">
<Flex direction="column">
<Text as="span" fontWeight={800}>
ニックネーム
</Text>
<Text as="span">
{user.attributes.nickname}
</Text>
</Flex>
変更
</Flex>
</Link>
<Link href="/user_edit">
<Flex direction="row" alignItems="flex-end" padding="0.5rem">
<Flex direction="column">
<Text as="span" fontWeight={800}>
メールアドレス
</Text>
<Text as="span">
{user.attributes.email}
</Text>
</Flex>
変更
</Flex>
</Link>
<Link href="/user_edit">
<Flex direction="row" alignItems="flex-end" padding="0.5rem">
<Flex direction="column">
<Text as="span" fontWeight={800}>
パスワード
</Text>
<Text as="span">
**********
</Text>
</Flex>
変更
</Flex>
</Link>
<Flex direction="row" alignItems="flex-end" padding="0.5rem">
<Text as="span" fontWeight={800}>
メールマガジンを受け取る
</Text>
<SwitchField
trackColor={tokens.colors.lightgray}
trackCheckedColor={tokens.colors.red}
//isChecked={true}
isDisabled={false}
label=""
labelPosition="start"
/>
</Flex>
</Flex>
</Flex>
</Flex>
</View>
↑大幅に変更
</>
);
}
export default UserPage;
UserEditPage.js
UserEditPage.jsを整えていく。更新後、/user に遷移するようnavigateメソッドを呼び出す。このあたりの細かい処理を解決するのに時間がかかってしまう。
■/src/pages/UserEditPage.js
import { useAuthenticator } from '@aws-amplify/ui-react';
import { useState } from "react";
import { Auth } from 'aws-amplify';
import { Flex, Button, TextField, Heading, Link, Icon, Divider } from '@aws-amplify/ui-react'; ←追加
import { useNavigate } from "react-router-dom"; ←追加
const UserEditPage = () => {
const { user, signOut } = useAuthenticator((context) => [context.user]);
const [nickname, setNickname] = useState(user.attributes.nickname);
const [email, setEmail] = useState(user.attributes.email); ←追加
const navigate = useNavigate(); ←追加
console.log(nickname); ←削除
async function updateUserInfo() {
let result = null;
try {
const user = await Auth.currentAuthenticatedUser();
result = await Auth.updateUserAttributes(user, {
nickname: nickname
});
} catch(error) {
console.log('error update: ', error);
}
if (result === 'SUCCESS') {
console.log("ユーザー情報を更新しました!"); ←変更
navigate('/user'); ←追加
}
else {
alert("ユーザー情報の更新に失敗しました・・・");
}
return result;
}
return (
↓大幅に修正
<Flex direction="column" gap="1rem" alignItems="flex-start">
<Flex direction="row">
<Link href="/user">
<Icon
pathData="M15.41 7.41 14 6l-6 6 6 6 1.41-1.41L10.83 12l4.58-4.59Z"
viewBox={{
width: 20,
height: 26,
}}
width="20"
height="26"
color="gray"
ariaLabel="back"
/>
</Link>
<Heading level={4}>アカウント変更</Heading>
</Flex>
<Divider orientation="horizontal" />
<Flex>
<TextField
descriptiveText=""
defaultValue={user.attributes.email}
label="メールアドレス"
errorMessage=""
width="20rem"
onChange={e => setEmail(e.target.value)}
/>
</Flex>
<Flex>
<TextField
descriptiveText=""
defaultValue={user.attributes.nickname}
label="ニックネーム"
errorMessage=""
width="20rem"
onChange={e => setNickname(e.target.value)}
/>
</Flex>
<Flex>
<Button size="small" onClick={() => window.location.href = '/user'}>キャンセル</Button>
<Button size="small" variation="primary" onClick={() => updateUserInfo()}>保存</Button>
</Flex>
</Flex>
↑大幅に修正
);
}
export default UserEditPage;
Layout.js
ハンバーガーメニューを整備していく。「お知らせ」「お気に入りの記事」などはダミーで設置しておく。<Outlet />に下層のコンポーネントを表示させる。
■/src/pages/Layout.js
import { Outlet } from 'react-router-dom';
import { useAuthenticator } from '@aws-amplify/ui-react';
import { Link, Heading, View, Menu, MenuItem, Divider, Flex, Badge } from '@aws-amplify/ui-react';
←コンポーネント追加
const Layout = () => {
const { route, signOut } = useAuthenticator((context) => [
context.route,
context.signOut,
]);
return (
<>
↓大幅に変更
<Flex direction="column" gap="1rem" alignItems="center">
<Heading level={3}><Link href="/">Twin kangaroos</Link></Heading>
</Flex>
<Flex direction="column" gap="1rem" alignItems="flex-end">
{
route === 'authenticated' ?
<Menu menuAlign="end" size="small">
<MenuItem onClick={() => alert('お知らせ画面に遷移予定・・・')}>
お知らせ <Badge size="small" variation="info">3</Badge>
</MenuItem>
<MenuItem onClick={() => alert('お気に入りの記事に遷移予定・・・')}>
お気に入りの記事
</MenuItem>
<MenuItem onClick={() => window.location.href = '/user'}>
アカウント設定
</MenuItem>
<Divider />
<MenuItem isDisabled onClick={() => alert('プレミアム会員専用ページです。')}>
プレミアム
</MenuItem>
<MenuItem onClick={signOut}>
ログアウト
</MenuItem>
</Menu>
:
<Menu menuAlign="end" size="small">
<MenuItem onClick={() => window.location.href = '/login'}>
ログイン
</MenuItem>
</Menu>
}
</Flex>
<Flex direction="column" gap="1rem" alignItems="center">
{
route !== 'authenticated' ?
<View padding="0.5rem">
ログインすると会員限定コンテンツを見ることができます。
</View>
:
''
}
<Outlet />
</Flex>
↑大幅に変更
</>
);
}
export default Layout;
完成した画面イメージ
完成した画面は以下のようになる。




まとめ
Amplify UI コンポーネントはリファレンスが比較的充実しているので、何度も熟読したが、非常に読みづらい。例えば、Flexコンポーネントの取り扱いであるが、divタグのfloatをどのように取り扱うのか理解が難しい。
公式のリファレンスを見ると、他にもいろいろなコンポーネントがあるので興味深い。
■Amplify UI – Build UI fast with Amplify on React
https://ui.docs.amplify.aws/react

実際に業務でReactでWebサイトを構築する際は、これらのコンポーネントを念頭においた上で設計を行う必要がありそうだ。
今回、cssの記述をすることなく、プロパティを設定することによりある程度のデザインの画面を構築できた。見た目のこだわりがある場合、さらなるカスタマイズが必要ではあるが、モックアップを短期間に作成するのであれば十分であるように思う。
残課題は以下のように残っているが、今回はここまでとする。
- 各ページにおいて、親ページ:Authenticatorタグ → 子ページ:実装、となっており冗長
- メールアドレス、パスワードの変更処理の実装
- 「メールマガジンを受け取る」カスタマイズ属性の追加
- 会員登録フローの途中で確認コードを受信後、期限が切れるとそのメールアドレスは使えなくなる
- 認証済みの場合に閲覧できる会員限定ページの作成
- お知らせの実装