Skip to content

Instantly share code, notes, and snippets.

View manabuyasuda's full-sized avatar

安田 学 manabuyasuda

View GitHub Profile
@manabuyasuda
manabuyasuda / buildAbsoluteUrl.ts
Created August 20, 2025 08:46
canonicalなどに使用するURLを組み立てるヘルパー関数
/**
* パスを配列で渡して絶対URLを安全に構築する
* 環境変数からベースURLを取得し、URL()コンストラクターを使用してURLエンコーディングと妥当性検証をする
* パスの先頭・末尾のスラッシュは自動的に正規化される
*/
export function buildAbsoluteUrl(pathSegments: string[]): string | null {
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL;
if (!baseUrl) return null;
@manabuyasuda
manabuyasuda / getParentMetadataProperty.ts
Created August 20, 2025 08:45
generateMetadataにあるparentを扱いやすくするヘルパー関数
/**
* 親セグメントから特定のメタデータを取得する
* 指定されたプロパティの存在するフィールドのみをフィルタリングして取得する
* @param parentMetadata 親セグメントから取得するメタデータ
* @param propertyName 取得したいプロパティ名('openGraph', 'alternates'など)
* @example
* import type { Metadata, ResolvingMetadata } from 'next';
* import { getParentMetadataProperty } from '@/lib/metadata/helpers';
*
* export async function generateMetadata(
@manabuyasuda
manabuyasuda / useSelectWidth.tsx
Created June 16, 2025 05:51
selectタグの幅を動的に計算するカスタムフック
'use client';
import { useLayoutEffect, useRef, useSyncExternalStore, useCallback } from 'react';
/**
* selectタグの幅を動的に計算するカスタムフックです。
* デフォルトではoptionタグにある最長のテキストに基づいて幅を計算しますが、これを上書きします。
* @param selectRef - selectタグへの参照
* @param currentValue - 現在選択されている値
* @returns 計算された幅(ピクセル単位)または null(SSR時)
@manabuyasuda
manabuyasuda / next.config.ts
Last active June 16, 2025 05:46
next.config.tsに設定しておくといいheaders
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
async headers() {
return [
{
source: '/(.*)',
headers: [
// frame-ancestors:他のドメインで自分たちのサイトをiframeで読み込むのを禁止させることで、iframeを使ったクリックジャッキングを防ぎます
// https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors
import React from 'react';
import DOMPurify from 'isomorphic-dompurify';
/**
* ユーザー投稿コンテンツをサニタイズし、安全にレンダリングする。
*
* @param {string | null | undefined} content - サニタイズおよびレンダリングするコンテンツ
* @returns {React.ReactNode} サニタイズされ、レンダリング可能なコンテンツ
*
* @example
@manabuyasuda
manabuyasuda / .svgrrc.js
Created May 13, 2025 09:23
SVGファイルをSVGRでコンポーネント化して共通Iconコンポーネントで管理する
module.exports = {
typescript: true, // .tsxファイルを生成する
memo: true, // Reactのメモ化を有効にする
jsxRuntime: 'automatic', // Reactの明示的なインポートをしない
filenameCase: 'kebab', // ファイル名をkebab-caseにする
index: false, // index.tsxファイルを生成しない
svgoConfig: {
plugins: [
{
name: 'preset-default', // 標準的なプリセットを使用する
@manabuyasuda
manabuyasuda / constants.ts
Last active July 7, 2025 02:05
microCMSの画像APIを利用して最適化した画像コンポーネント
import { SupportedImageFormat } from './types';
/**
* w記述子で生成する画像サイズパターン
* https://nextjs.org/docs/pages/api-reference/components/image#advanced
*/
export const SRCSET_SIZES = [16, 32, 48, 64, 96, 128, 256, 384, 640, 750, 828, 1010];
/**
* 画像の初期設定値
import { usePathname, useSearchParams } from 'next/navigation';
import { useCallback, useEffect, useRef, useMemo } from 'react';
type ScrollIntoViewOptions = {
behavior?: ScrollBehavior;
block?: ScrollLogicalPosition;
inline?: ScrollLogicalPosition;
};
interface UseLinkScrollOptions {
import { useEffect, useCallback, useRef } from 'react';
// ブラウザ環境かどうかを判定する
const isBrowser = typeof window !== 'undefined';
// iOSデバイスかどうかを判定する
const isIosDevice =
isBrowser &&
window.navigator &&
window.navigator.platform &&
import { useEffect, useCallback, useRef, useState } from 'react';
/**
* 開閉処理以外のオプション設定。
*/
type ModalOptions = {
/** Escキーでモーダルを閉じるかどうか @default true */
closeOnEsc?: boolean;
/** フォーカストラップを有効にするかどうか @default true */
enableFocusTrap?: boolean;