2025-01-13
Reactでポートフォリオサイトを作成する 🚀(2)
ReactNext.jsポートフォリオ
はじめに
「自分にとって新しい技術をインプットしたい」と「長く残るアウトプットを作りたい」という思いから、Reactを使ってポートフォリオサイトの作成に挑戦してみることにしました。
この記事を含め、複数回に分けて作成過程を記録していきます。ここまでの記事は以下です。
今回は以下の内容についてご紹介します。
- 【ヘッダー】TOP画面とそれ以外の画面で表示を分ける
- 【プロフィール画面】プロフィールを記載する箱を作成
- 【プロフィール画面】ページタイトルなどをコンポーネント化
【ヘッダー】TOP画面とそれ以外の画面で表示を分ける
以下記事を参考に、ホーム画面以外の時はホーム画面へのリンクを左側に表示させるようにする
usePathnameを利用する時は`"use client"を追加してクライアントコンポーネントにする必要がある
Header.tsx
'use client'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
const Header = () => {
const pathname = usePathname()
const isMainPage = pathname === '/'
return (
<header className="pt-5 pl-5 mb-20">
<div className="container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center">
{!isMainPage && (
<a
href="/"
className="flex font-mobo font-medium items-center mb-4 md:mb-0"
>
<span className="ml-3 text-xl">Motoshi Furugen</span>
</a>
)}
<nav className="md:ml-auto flex flex-wrap items-center text-base justify-center font-mobo">
<Link className="mr-10 hover:opacity-50" href="/profile">
プロフィール
</Link>
<Link className="mr-10 hover:opacity-50" href="#">
開発ブログ
</Link>
<Link className="mr-10 hover:opacity-50" href="#">
実績
</Link>
<Link className="mr-10 hover:opacity-50" href="#">
コンタクト
</Link>
<Link className="mr-10 hover:opacity-50" href="#">
ソースコード
</Link>
</nav>
</div>
</header>
)
}
export default Header
ヘッダーを上部に固定させる。headerに"fixed"を追加して、main要素にpadding-topを追加する。
Header.tsx
<header className="fixed top-0 left-0 w-full z-50 py-5 pl-5 mb-20 bg-bg-main">
layout.tsx
<html lang="en">
<body className={inter.className}>
<Header />
<main className="pt-40">{children}</main>
</body>
</html>
Githubへのリンクを設置する
Header.tsx
<Link
className="mr-10 hover:bg-gray"
href="https://github.com/motoshifurugen/my_site"
target="_blank"
rel="noopener noreferrer"
>
<div className="flex items-center border border-gray-300 rounded px-3 py-1">
<FontAwesomeIcon icon={faGithub} className="mr-2" />
ソースコード
</div>
</Link>

【プロフィール画面】画面作成
MainMessege
コンポーネント内に入れていたプロフィールへのリンク部分はpage.tsx
に一旦移し、メインメッセージをプロフィール画面でも使うことにする。/pofile/page.tsx
import MainMessage from '../components/MainMessage'
export default function Page() {
return (
<section className="profile px-20">
<div className="flex justify-center h-screen">
<MainMessage />
</div>
</section>
)
}
プロフィール欄を用意して、左に説明・右に写真を配置する。
/pofile/page.tsx
<section className="profile px-20">
<h1 className="text-4xl font-bold">プロフィール</h1>
<div className="flex justify-center">
<MainMessage />
</div>
<div>
<h2 className="text-3xl font-bold">経歴</h2>
<div className="flex p-10">
<p className="flex w-1/2 text-lg leading-loose items-center">
<p className="mr-6 text-right">
1998年
<br />
2017年
<br />
2020年
<br />
2021年
<br />
2023年
<br />
現在
</p>
<p>
沖縄に生まれる
<br />
高校卒業後、広島大学理学部物理学科へ進学する
<br />
大学休学中にプログラミングを始める
<br />
長期インターンでWebエンジニアを経験する
<br />
大学卒業後、エンジニアとして就職する
<br />
フロントエンドエンジニアとして奮闘中
</p>
</p>
<div className="w-1/2 flex justify-end">
<Image
src="/images/etsushi.jpg"
alt="profile img 01"
width={500}
height={500}
/>
</div>
</div>
</div>
</section>

プロフィールで使用した3つの紹介(経歴・興味・趣味)の記述部分をコンポーネント化する。
Card.tsx
// components/Card.tsx
import { ReactNode } from "react";
import Image from "next/image";
interface CardProps {
title: string;
content: ReactNode;
imageSrc: string;
imageAlt: string;
}
const Card: React.FC<CardProps> = ({ title, content, imageSrc, imageAlt }) => {
return (
<div className="card">
<h2 className="text-3xl font-bold">{title}</h2>
<div className="flex p-10 font-open-sans">
<div className="flex w-1/2 text-lg leading-loose items-center">
{content}
</div>
<div className="w-1/2 flex justify-end">
<Image src={imageSrc} alt={imageAlt} width={500} height={500} />
</div>
</div>
</div>
);
};
export default Card;
profile/page.tsx
にて、Card
コンポーネントを使用する。profile/page.tsx
<Card
title="経歴"
content={
<>
<p className="mr-6 text-right">
1998年<br />
2017年<br />
2020年<br />
2021年<br />
2023年<br />
現在
</p>
<p>
沖縄に生まれる<br />
高校卒業後、広島大学理学部物理学科へ進学する<br />
大学休学中にプログラミングを始める<br />
長期インターンでWebエンジニアを経験する<br />
大学卒業後、エンジニアとして就職する<br />
フロントエンドエンジニアとして奮闘中
</p>
</>
}
imageSrc="/images/profile_01.jpg"
imageAlt="profile img 01"
/>
<Card
title="興味"
content={
<>
<p>
物理学が目に見えない自然の法則を解き明かすように、<br />
データという見えない情報を扱うことに楽しさを感じています。<br />
最近はバックエンドやネットワーク分野に興味があり、<br />
今年はネットワークスペシャリストの資格に挑戦します。<br />
心が強い方ではないので、メンタルヘルスへの関心も大切にしています。
</p>
</>
}
imageSrc="/images/profile_02.png"
imageAlt="profile img 02"
/>
<Card
title="趣味"
content={
<>
<p>
エイサー(沖縄の伝統芸能) ・ 読書(ビジネス書中心) ・<br />
散歩 ・ 短歌 ・ ギター(アコギ) ・ ドライブ ・ ボウリング <br />
and more<br />
</p>
</>
}
imageSrc="/images/profile_03.jpg"
imageAlt="profile img 03"
/>
【プロフィール画面】ページタイトルなどをコンポーネント化
画面説明部分をコンポーネントに切り分ける
PageFace.tsx
import React from 'react';
interface PageFaceProps {
title: string;
subtitle: string;
mainMessage: React.ReactNode;
}
const PageFace: React.FC<PageFaceProps> = ({ title, subtitle, mainMessage }) => {
return (
<div className="flex mb-24">
<div className="w-1/2">
<h1 className="text-4xl font-bold">{title}</h1>
<h2 className="text-2xl font-bold mt-5">{subtitle}</h2>
</div>
<div className="flex justify-left w-1/2">
{mainMessage}
</div>
</div>
);
};
export default PageFace;
/profile/page.tsx
<PageFace
title="プロフィール"
subtitle="古堅基史(Furugen Motoshi)"
mainMessage={<MainMessage />}
/>
PageFaceコンポーネントの下部に直線を表示させる。(アニメーション付き)
PageFace.tsx
<div
ref={lineRef}
className="h-0.5 opacity-50 bg-font-main transition-all duration-1000 ease-in-out w-0 mt-4 mb-24"
></div>

to be continued...
次回はスキル・実績画面の作成を行います。