【Next.js】viewportを使って小さい画面幅のレスポンシブ対応をする方法

はじめに

こんにちは、株式会社TOKOSのツキヤです!
今回は、フロントエンドを実装する時に気をつける「レスポンシブ対応」について、特に小さい画面幅の際の効果的な対応方法を説明します✍️
どうしても小さい画面幅のレスポンシブ対応がしづらい場合はこの記事が役立つかもです✨

本記事はNext.jsで記述しますが、素のReactやReimix等他のReactフレームワークでも対応可能なはずなのでぜひ参考にしてみてください💪

対象読者・前提

  • Next.js (React) で小さい画面幅の対応をサクッとしたい方
  • viewportについて知りたい方

今回は、Next.js の App Routerを用いて、410px以下の画面幅を対応するものとします!

実装方針

実装方針としては、「特定のpx以下の場合はviewportのwidthを固定にする」というものです!
上記方針とそれに関わる知識の説明をサクッとします!

viewportとは?

HTMLを記述したことがある人なら、<head>タグの中で1度は見かけたことがある
<meta name="viewport" content="width=device-width, initial-scale=1.0">
↑ この name="viewport" となってるmetaタグを本記事ではviewportと呼んでいます!
これは、HTMLがデバイス上でどのように表示されるかをブラウザに指示するためのタグです!
このmetaタグがあることで、ウェブページは異なる画面サイズ(デバイス)に適応し、見やすくなります。

特に理解しておいてほしいのが、 content="width=device-width の部分です!
この設定は、ビューポートの幅(ページが表示される領域の幅)を、デバイスの画面幅に合わせるというものです。この設定により、ページはデバイスの画面幅に基づいて適切に縮尺され、ユーザーが画面をズームしなくても内容全体を見ることができるようになります✨

ツキヤ
ツキヤ

当たり前だけど、widthが390pxのiPhone 14で見たら390pxに、1440pxのMacbookで見たら1440pxになるということだね!

initial-scale=1.0 は、ページにアクセスした時のズームの具合です!
これは100%(デフォルト) であってほしいので、ほとんどのサイトで1.0が設定されているはずです。

実装方法

上記を理解した上で、改めて今回の方針である「特定のpx以下の場合はviewportのwidthを固定にする」という実装を行います!
と言っても簡単で、1つコンポーネントを作るだけです!
今回は適当なパスに ControlViewport.tsx というコンポーネントを作成します。

"use client"

import { useEffect } from "react"

export function ControlViewport(): null {
  useEffect(() => {
    if (typeof window === "undefined") return
    const resizeViewport = () => {
      const viewport = document.querySelector("meta[name=viewport]")
      if (!viewport) return
      viewport.setAttribute(
        "content",
        window.outerWidth > 410
          ? `width=device-width,initial-scale=1.0,maximum-scale=1.0`
          : `width=410,maximum-scale=1.0`,
      )
    }

    window.addEventListener("resize", resizeViewport)
    return () => window.removeEventListener("resize", resizeViewport)
  }, [])

  return null
}

上記コンポーネントは、 return null をしているので、UIには特に影響を与えません。

useEffect 内で、 document.querySelector("meta[name=viewport]") を使ってmetaタグを取得した後に、 window.outerWidth という現在のブラウザの横幅を取得するメソッドを使って、
・410pxより大きい: width=device-width
・410px以下   : width=410
上記分岐を行い、metaタグにセットし直しているだけです😎

ツキヤ
ツキヤ

アンマウント時のためのクリーンアップ関数も忘れずに!

このコンポーネントをlayout.tsxで呼び出します。

import { ControlViewport } from "@/app/_components/ControlViewport"

const RootLayout = ({ children }: { children: React.ReactNode }) => (
  <html lang="ja">
    <body>
      <ControlViewport />
      {children}
    </body>
  </html>
)
export default RootLayout

以上で、410px以下の画面幅の場合は等倍で縮尺するようになります!

終わりに

今回は、小さい画面幅に対する簡単なレスポンシブ対応の方法を説明しました!
とは言え、一般的なスマホのサイズ(400px前後)位に対しては普通のレスポンシブ対応をすべきです。
今回の方法は、特に小さな画面幅に対してのコストパフォーマンスを考慮に入れた上で実装を検討してみてください🙏