I create my own homepage!
Tags: portfolio, nextjs, graphql, showdev
Published: 2022-05-22 07:52:37

## TL;DR
Rebuilt portfolio.

1. High Speed
2. Scalable
3. State-of-the-art technology

https://re-taro.dev <- This is the Portfolio I build!

https://github.com/re-taro/re-taro.dev <- And this is the repository!

## High Speed

We are using [Next.js](https://nextjs.org) as our framework to do SG.

It's frustrating to be Vercel's dog, but I'll put up with it. It is because it is attractive to be able to optimize font and images easily.

Although it is called SG, JS runs at each page transition, so the movement is smooth (in other words, it is not pure).

[Cloudinary](https://cloudinary.com) is used for image management to deliver optimized images.

The size of the images is directly related to the display speed, so it is very helpful.

## Scalable

### Throughout

The component design is based on [Atomic Design](https://atomicdesign.bradfrost.com), a practice known as Component Driven Development, so it is easy to reuse and extend.

### Web Page

In order to keep the information as up-to-date as possible and not to leave direct commits to [this repository](https://github.com/re-taro/re-taro.dev/) when changing information, an API server was built and information was exchanged through it.

### Blog Page

Markdown (content management) + tsx (template engine); Ramanujan said that Markdown would not go out of date so easily and that he felt comfortable throwing it into another service someday (I don't know, but...).

The Markdown processor used [unified](https://github.com/unifiedjs/unified) assets.

It was fun because it was built using a combination of various unified APIs.

## State-of-the-art technology

I like it. Yes, I like state-of-the-art technology. Below is an excerpt from my club's LT talk, listing the technologies used in this portfolio.

![ナウくね？](https://res.cloudinary.com/re-taro/image/upload/q_60/f_auto/v1653117372/posts/new-portfolio/new-portfolio1_ezd66i.png)

### Front-end

Next.js for framework, [Tailwind CSS](https://tailwindcss.com) for styling (but I wanted to use CSS in JS, so I used [twin.macro](https://github.com/ben-rogerson/twin.macro) this time).

I'm using Urql as my GraphQL client.

Hosting is by Vercel (I'm a dog, woof woof).

### Back-end

I'm using Juniper. You may have noticed that I am trying to write a GraphQL server in [Rust](https://www.rust-lang.org).

It's difficult again. I want to escape to [Nest.js](https://nestjs.com) right away.

I'm leaving it to [render](https://render.com) to deploy to (Heroku? That guy is...). I'm still trying to figure out what to do with GraphQL.

GraphQL is a justice, I thought while making it.

### Others

I'm trying to use [umami](https://umami.is) this time for analytics. The UI is really cute and I love it.

## Feature

In the following, we will describe the specific functions we were able to implement and the libraries we used.

### Tailwind CSS & twin.macro

I don't think Tailwind CSS is justice. However, I think it is one of the solutions.

Also, since I adopted Atomic Design, I thought that developing with CSS in JS was the best choice, so I used twin.macro.

There was one major problem with twin.macro. It does not support Tailwind CSS v3.

The [issue](https://github.com/ben-rogerson/twin.macro/issues/589) is still standing and is being monitored.

The `flex-basis` and other features added since Tailwind CSS v3 are naturally not supported, so the following action was taken.

![ゴリ押しは正義](https://res.cloudinary.com/re-taro/image/upload/q_60/f_auto/v1653123753/posts/new-portfolio/new-porfolio2_n4kgtd.png)

### Urql

What do you think of GraphQL client libraries? Some well-known ones are Apollo and Relay. There is a library called `@next/bundle-analyzer`. Let's build it and look at the bundle size.

You will be surprised at the size of Apollo and Relay. You will be surprised at the size of Apollo and Relay, and how small Urql is in comparison. If you're curious, [Urql does a comparison here](https://formidable.com/open-source/urql/docs/comparison/).

Of course, it is the right size for the right place. In this case, I only needed a query, so I made it as small as possible.

```tsx
/* pages/index.tsx */

// 略

// eslint-disable-next-line unicorn/prevent-abbreviations
export const getStaticProps: GetStaticProps<{ meta: SeoProperties; urqlState: SSRData }> = async () => {
  const client = await urqlClient();
  await client.query(HomeDocument).toPromise();
  const meta: SeoProperties = {
    description: "Rintaro Itokawa's Dev Site | re-taro",
    ogImageUrl: encodeURI(`${OGP_HOST}/api/ogp?title=re-taro`),
    pageRelPath: "",
    pagetype: "website",
    sitename: "re-taro.dev",
    title: "Rintaro Itokawa - Emotion Seeker",
    twcardtype: "summary_large_image",
  };
  return {
    props: {
      meta,
      urqlState: ssrCache.extractData(),
    },
  };
};

const HomePage: NextPage<Properties> = ({ meta }) => {
  const [response] = useQuery<HomeQuery>({ query: HomeDocument });
  return <Home data={response.data} meta={meta} />;
};

export default withUrqlClient(
  () => ({
    url: END_POINT,
  }),
  { neverSuspend: true, ssr: false },
)(HomePage);
```

It was very simple and easy to use, although a little unusual to use.

### GitHub Flavored Markdown

Supported by `remark-gfm`.

```md
| Create     | Table    |
| -------- | ---------- |
| For | Example |
| increase   | Element.   |

- I can write
  - list. Also, [^1]

[^1]: Footnotes can be used.
```
| Create     | Table    |
| -------- | ---------- |
| For | Example |
| increase   | Element.   |


- I can write
  - list. Also, [^1]

### Emoji

Convert with `remakr-gemoji`.

`:v:` convert to :v:.

### Numerical Formula

Bite the `remark-math` and `rehype-katex`.

It is easy to set up just by loading the style sheet.

```tsx
// components/organisms/post-meta/index.tsx

// 略

const PostMeta: React.FC<PostMetaPropeties> = ({ meta }) => (
  <React.Fragment>
    <Seo {...meta} />
    <Head>
      <link
        rel={"stylesheet"}
        href={"https://cdn.jsdelivr.net/npm/katex@0.15.6/dist/katex.min.css"}
        integrity={"sha384-ljao5I1l+8KYFXG7LNEA7DyaFvuvSCmedUf6Y6JI7LJqiu8q5dEivP2nDdFH31V4"}
        crossOrigin={"anonymous"}
      />
      <title></title>
    </Head>
  </React.Fragment>
);

// 略
```

### ruby

I used remark-jaruby created by [@haxibami](https://github.com/haxibami).

The chain of methods around `remark-parse` / `remark-rehype` is as follows...

```tsx
// utils/parser.ts

import rehypeShiki from "@re-taro/rehype-shiki";
import rehypeAutolinkHeadings from "rehype-autolink-headings";
import rehypeKatex from "rehype-katex";
import rehypeSlug from "rehype-slug";
import rehypeStringify from "rehype-stringify";
import remarkGemoji from "remark-gemoji";
import remarkGfm from "remark-gfm";
import remarkJaruby from "remark-jaruby";
import remarkMath from "remark-math";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import remarkToc from "remark-toc";
import remarkUnwrapImages from "remark-unwrap-images";
import * as shiki from "shiki";
import stripMarkdown from "strip-markdown";
import { unified } from "unified";

const MdToHtml = async (md: string) => {
  const result = await unified()
    .use(remarkParse)
    .use(remarkGfm)
    .use(remarkGemoji)
    .use(remarkMath)
    .use(remarkJaruby)
    .use(remarkUnwrapImages)
    .use(remarkToc, {
      heading: "目次",
      tight: true,
    })
    .use(remarkRehype)
    .use(rehypeKatex)
    .use(rehypeShiki, {
      highlighter: await shiki.getHighlighter({ theme: "nord" }),
    })
    .use(rehypeSlug)
    .use(rehypeAutolinkHeadings, {
      behavior: "wrap",
    })
    .use(rehypeStringify)
    .process(md);
  return result.toString();
};

export { MdToHtml }
```

In addition, the `rehype-react` related process is as follows.

```tsx
// lib/rehype-react.ts

import React from "react";
import rehypeParse from "rehype-parse";
import rehypeReact from "rehype-react";
import type { Options as RehypeReactOptions } from "rehype-react";
import { unified } from "unified";
import { Image } from "~/components/molecules/image";
import type { ImageProperties } from "~/components/molecules/image";
import { Link } from "~/components/molecules/link";
import type { LinkProperties } from "~/components/molecules/link";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const RehypeReact = (html: string): React.ReactElement<unknown, string | React.JSXElementConstructor<any>> => {
  const result = unified()
    .use(rehypeParse, {
      fragment: true,
    })
    .use(rehypeReact, {
      components: {
        // eslint-disable-next-line id-length
        a: (properties: LinkProperties) => Link(properties),
        img: (properties: ImageProperties) => Image(properties),
      },
      createElement: React.createElement,
    } as RehypeReactOptions)
    .processSync(html);
  return result.result;
};

export { RehypeReact };
```

The above made the blog very easy to write.

### dynamic OGP

I deployed to Vercel while looking at [@haxibami](https://github.com/haxibami)'s implementation.

https://github.com/re-taro/ogp.re-taro.dev <- This is the repository!

![OGP](https://res.cloudinary.com/re-taro/image/upload/q_60/f_auto/v1653141535/posts/new-portfolio/new-porfolio3_pdt0ou.png)

### CI/CD

When [the repository where the data](https://github.com/re-taro/re-taro.d) is stored is updated, actions fires and sends a dispatch event to [the portfolio repository](https://github.com/re-taro/re-taro.dev), and the one that receives the dispatch event automatically builds the data. I like it, it's quite stylish.

Dispatcher

```yaml
name: Dispatch
on: push

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: |
          curl -vv -H "Authorization: token ${{ secrets.DISPATCH_TOKEN }}" -H "Accept: application/vnd.github.everest-preview+json" "https://api.github.com/repos/re-taro/re-taro.dev/dispatches" -d '{"event_type": "update"}'
```

Receiver of the dispatch event

```yaml
name: Dispatch production build

on:
  repository_dispatch:
    types: [update]

jobs:
  // Jobs you want to run
```

## Impressions

I am proud to say that it turned out crazy good.

[^1]: Footnotes can be used.