フロントエンド開発といえば。
next.js アプリの初期化( npx create-next-app@latest --use-npm <アプリ名> )

next.js で .envにtypescript 型定義をつける

src/types/env.d.ts ( ファイル名やディレクトリはどこでもokです )

declare namespace NodeJS {
  interface ProcessEnv {
    readonly APP_NAME: string;
    readonly NEXT_PUBLIC_APP_NAME: string;
  }
}

.env

# APP
APP_NAME=アプリ名
NEXT_PUBLIC_APP_NAME=${APP_NAME}

引用 : https://bit.ly/3OcVU7p

No.2344
05/15 09:33

edit

npm script で ブラウザを開く

● npm script で ブラウザを開く

・1. npm-run-all , opener のインストール

npm install --save-dev npm-run-all opener 

package.json

  "scripts": {
    "dev": "npm-run-all --parallel dev:next dev:browser",
    "dev:next": "next dev -p 3999",
    "dev:browser": "sleep 1 && opener http://localhost:3999/",
  },

実行

npm run dev

npm-run-allのオプション

順次実行
「npm-run-all --serial」または「npm-run-all -s」または「run-s」と記述します
並列実行
「npm-run-all --parallel」または「npm-run-all -p」または「run-p」と記述します
No.2337
05/08 08:14

edit

Next.js で emotion

・ next.jsアプリの初期化

npx create-next-app@latest --use-npm next-emotion-minimal-app

・ emotion のインストール

npm install @emotion/react

・ tsconfig の設定

{
  "compilerOptions": {

    // emotion
    "jsx": "preserve",
    "jsxImportSource": "@emotion/react",

・ emotion を 使ったCSSスタイリングの例 (1. jsx に直接記述)

src/pages/index.tsx

import { css } from '@emotion/react';

export default function Home() {
  return (
    <>
      <div
        css={css`
          color: red;
          font-size: 48px;
          font-weight: bold;
        `}
      >
        home
      </div>
    </>
  );
}

・ emotion を 使ったCSSスタイリングの例 (2. 変数を2つ定義して配列でマージする)

import { css } from '@emotion/react';

const fontLarge = css`
  font-size: 48px;
`;

const colorGreen = css`
  color: green;
`;

export default function Home() {
  return (
    <>
      <div css={[fontLarge, colorGreen]}>home</div>
    </>
  );
}

・ emotion を 使ったCSSスタイリングの例 (変数定義時に他のスタイルを取り込む)

import { css } from '@emotion/react';

const fontLarge = css`
  font-size: 48px;
`;

const myFont = css`
  ${fontLarge}
  color: blue;
`;

export default function Home() {
  return (
    <>
      <div css={myFont}>home</div>
    </>
  );
}

・ emotion を 使ったCSSスタイリングの例 (グローバルスタイルを 設定する)

import type { AppProps } from 'next/app';
import { css, Global } from '@emotion/react';

const globalStyle = css`
  body {
    font-family: 'Helvetica Neue', Arial, 'Hiragino Kaku Gothic ProN',
      'Hiragino Sans', Meiryo, sans-serif;
  }
`;

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Global styles={globalStyle}></Global>
      <Component {...pageProps} />
    </>
  );
}

・ emotion を 使ったCSSスタイリングの例 (動的スタイル conditional style)

src/pages/index.tsx

import { useState } from 'react';
import { css } from '@emotion/react';

type MyFont = {
    colored:boolean
}

export default function Home() {
  const [colored, setColored] = useState<boolean>(false);

  const myFont = ({ colored }:MyFont) => css`
    border: solid 1px black;
    border-radius: 10px;
    padding: 16px;
    cursor: pointer;
    ${colored &&
    `
    background-color: #413F42;
    color: white;
  `}
  `;

  return (
    <>
      <button
        onClick={() => {
          setColored(!colored);
        }}
      >
        色変更
      </button>
      <div css={myFont({ colored: colored })}>home</div>
    </>
  );
}

・ emotion を 使ったCSSスタイリングの例 (styled-components のように設定する)

npm install @emotion/styled

こちらで css から js object に 変換します

https://transform.tools/css-to-js

src/pages/index.tsx

import styled from '@emotion/styled';

const MyTitle = styled.h1`
  font-size: 3.5rem;
  margin: 0px;
  color: blue;
`;

export default function Home() {
  return (
    <>
      <MyTitle as={'button'}>home</MyTitle>
    </>
  );
}

as={'button'} で 出力時にタグを h1 から button に変更しています

@emotion/reactでのスタイル指定方法 - Qiita

No.2319
05/15 12:43

edit

zustand

● zustandのインストール

npm i zustand

● storeの作成

src/stores/book.ts

import create from "zustand";

type BookStoreData = {
  amount: number;
  title: string;
};

const initialData: BookStoreData = {
  amount: 0,
  title: "",
};

interface BookStore {
  book: BookStoreData;
  updateAmount: (newAmount: number) => void;
  updateTitle: (newTitle: string) => void;
  fetchTitle: () => void;
}

export const useBookStore = create<BookStore>((set, get) => ({
  book: { ...initialData },

  updateAmount: (newAmount: number) => {
    const amountState = get().book.amount;
    const newBook: BookStoreData = {
      amount: newAmount + amountState,
      title: get().book.title,
    };
    set({ book: newBook });
  },

  updateTitle: (newTitle: string) => {
    const titleState = get().book.title;
    const newBook: BookStoreData = {
      amount: get().book.amount,
      title: newTitle,
    };
    set({ book: newBook });
  },

  fetchTitle: async () => {
    await new Promise((resolve) => setTimeout(resolve, 2000));
    const newBook: BookStoreData = {
      amount: get().book.amount,
      title: "fetched Title",
    };
    set({ book: newBook });
  },
}));

● コンポーネントで使用する

ストア値の参照 :  const book = useBookStore((state) => state.book);
アクションfetchTitle の呼び出し : const fetchTitle = useBookStore((state) => state.fetchTitle);

const BookSample = () => {
  const book = useBookStore((state) => state.book);
  const updateAmount = useBookStore((state) => state.updateAmount);
  const updateTitle = useBookStore((state) => state.updateTitle);
  const fetchTitle = useBookStore((state) => state.fetchTitle);
  const inputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    fetchTitle();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [fetchTitle]);

  return (
    <div>
      <h1> BookTitle: {book.title} </h1>
      <h1> Books: {book.amount} </h1>
      <button onClick={() => updateAmount(10)}> Update Amount </button>
      <div>
        <input type="text" ref={inputRef} />
        <button
          onClick={() =>
            updateTitle(inputRef.current ? inputRef.current.value : "")
          }
        >
          Update Text
        </button>
      </div>
    </div>
  );
};
No.2309
06/05 10:57

edit

すでに作成ずみの Next.js アプリを Firebase Hosting へデプロイする

● すでに作成ずみの Next.js アプリを Firebase Hosting へデプロイする

( SSR は Firebase Functionsへ。)

1. アプリの作成

npx create-next-app@latest --use-npm <アプリ名>

2. firebase コンソールからプロジェクトを作成する

https://console.firebase.google.com/ からプロジェクトを作成します。プロジェクト名はアプリ名と同じとしておくと良いでしょう。

3. package.json を変更する

以下をそれぞれの場所に追記します。

{
  "main": "firebaseFunctions.js",
  "scripts": {
    "serve": "npm run build && firebase emulators:start --only functions,hosting",
    "shell": "npm run build && firebase functions:shell",
    "deploy": "firebase deploy --only functions,hosting",
    "logs": "firebase functions:log"
  },
  "dependencies": {
    "firebase-admin": "^9.4.2",
    "firebase-functions": "^3.13.1",
  },
  "devDependencies": {
    "firebase-functions-test": "^0.2.3",
    "firebase-tools": "^9.3.0"
  }
}

その後にパッケージをインストールします

npm install

4. .firebaserc を追加する

vi .firebaserc
{
  "projects": {
    "default": "<アプリ名>"
  }
}

5. firebaseFunctions.js を追加する

vi firebaseFunctions.js
const { https } = require('firebase-functions')
const { default: next } = require('next')

const nextjsDistDir = './.next/';

const nextjsServer = next({
  dev: false,
  conf: {
    distDir: nextjsDistDir,
  },
})
const nextjsHandle = nextjsServer.getRequestHandler()

exports.nextjsFunc = https.onRequest((req, res) => {
  return nextjsServer.prepare().then(() => nextjsHandle(req, res))
})

5. firebase.json を追加する

vi firebase.json
{
  "hosting": {
    "public": "public",
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
    "rewrites": [
      {
        "source": "**",
        "function": "nextjsFunc"
      }
    ]
  },
  "functions": {
    "source": ".",
    "predeploy": [
      "npm --prefix \"$PROJECT_DIR\" install",
      "npm --prefix \"$PROJECT_DIR\" run build"
    ],
    "runtime": "nodejs16"
  },
  "emulators": {
    "functions": {
      "port": 5001
    },
    "hosting": {
      "port": 5002
    },
    "ui": {
      "enabled": true
    },
    "singleProjectMode": true
  }
}

6. ローカル(エミュレーター)で起動する

npm run serve

http://localhost:5002/

7. firebase console から設定を変更する

・プランを Blaze にアップグレードする
・Firebase Hosting を開始する

8. firebase へ デプロイする

npm run deploy

9. HTTP Error: 403, Project '<プロジェクト名>' not found or permission denied. エラーとなる場合の対処法

Project ID が 正しくない可能性があります。以下のコマンドからプロジェクトIDを確認します。

firebase projects:list

プロジェクトIDが間違っている場合は、次のファイル内のプロジェクトIDを正しいものに書き変えます。 .firebaserc

No.2295
03/13 23:53

edit

テンプレートを使ってNext.js を Firebase Hosting へデプロイする ( SSR は Firebase Functionsへ )

● 1. テンプレートを使って「Next.js を Firebase Hosting へ手動デプロイ」の練習をする

なお、SSR は Firebase Functions としてデプロイされるのでSSR可能です。

1. アプリの作成

例: アプリ名 my-test-hosting-app としています

npx create-next-app@latest --use-npm --example with-firebase-hosting my-test-hosting-app

2. Firebase のコンソールから 新規プロジェクトを作成

プロジェクト名はアプリ名と同じ my-test-hosting-app とするとわかりやすいです。 プロジェクト名はコピーしてクリップボードに保存しておきます

3. firebase cli からログイン

アプリのルートディレクトリに移動して、そこからコマンドでログインします。(間違いがないように先にログアウトしておきます)

firebase logout
firebase login

.firebaserc を変更する

{
  "projects": {
    "default": "先ほど保存したプロジェクト名をここにペースト"
  }
}

プロジェクト名をコピーし、忘れた時は、次のコマンドで一覧を表示させて、そこからコピーします

firebase projects:list

4. ローカルのテスト

firebase.json

ポートを 5002番に変更します

emlators を追加します

{
  "hosting": {
        ........
  } ,
  "functions": {
        ........
  } ,

  "emulators": {
    "functions": {
      "port": 5001
    },
    "hosting": {
      "port": 5002
    },
    "ui": {
      "enabled": true
    },
    "singleProjectMode": true
  }
}

package.json には

"serve": "npm run build && firebase emulators:start --only functions,hosting",

があるので このスクリプトを実行します。

npm run serve

5.ローカルサーバーへアクセスします

アプリ(ローカル)
http://localhost:5002/

firebase コンソール(ローカル)
http://localhost:4000/

6. 本番サーバ ( Firebase Hosting ) へデプロイする

functions の node.js のバージョンを16に設定します

firebase.json

  "functions": {
    ........
    "runtime": "nodejs16"
  },

npm run deploy

● 2. githubへ mainブランチを push したときに自動で Firebase にデプロイする設定を追加する

Firebase のシークレットを githubへ登録する

(シークレットの登録)1. Firebase コンソールからサービスアカウントの作成

Firebase コンソールの歯車アイコン → プロジェクトの設定 → サービスアカウント → 新しい秘密鍵の生成

で秘密鍵をダウンロードします。

(シークレットの登録)2. Githubへサービスアカウントの登録

Githubでアプリのリポジトリへ移動 → settings → Secrets and variables → Actions →「New repository secret」
Name : FIREBASE_SERVICE_ACCOUNT_<FirebaseプロジェクトIDを大文字で。>
Secret : ダウンロードした秘密鍵のJSONを貼り付ける

(シークレットの登録)3. Githubで Workflow の Permission を変更する

Githubでアプリのリポジトリへ移動 → Settings → Actionsの中のGeneral → General → Workflow permissions を以下の画像のように設定する

GitHub CLI のインストール

brew install gh

secretsの確認

gh secret list
vi .github/workflow/firebase-hosting-merge.yml

参考 : GitHub Actionsでfirebase preview channelを作成する - すな.dev

添付ファイル1
No.2294
03/13 23:30

edit

添付ファイル

firebase hosting

● Firebase CLI ( firebase-tools ) のインストール

npm install -g firebase-tools
firebase --version

firebase アカウントが既にある場合は一度ログアウトしてからログインしなおすと良いです

firebase logout
firebase login

firebase init hosting

アプリのディレクトリに移動してFirebaseをインストールする

npm install firebase
No.2292
03/09 18:26

edit

● Next.js のコンポーネントが 「サーバー」/「クライアント」どちらかのみで動作することを限定する

● server-only コンポーネントのインストール

npm i server-only

● サーバーサイドのみに限定する

import "server-only";

を 先頭に記述します。 これをクライアントで描画すると以下のようなエラーがスローされます。

● クライアントサイドのみに限定する

(サーバーサイドで実行された時にエラーがスローされます)

import "client-only";
添付ファイル1
No.2290
03/08 23:08

edit

添付ファイル

Next.js で 多言語

● A. ディレクトリで 言語ごとに分ける方法

/ja/ または ロケールなし の場合は 日本語
/en/ の場合は 英語
とするには以下のように記述します

next.config.js

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  i18n: {
    locales: ["en", "ja"],
    defaultLocale: "ja",
  },
};

module.exports = nextConfig;

これで

http://localhost:3000/login の場合は日本語
http://localhost:3000/ja/login の場合は日本語
http://localhost:3000/en/login の場合は英語

となります。

● コンポーネントでロケールを取得する

import { usePathname } from 'next/navigation';
const getLocale = (path: string): string => {
  if (path.match(/^\/en/)) return 'en';

  return 'ja';
};


const pathname = usePathname();
const locale = getLocale(pathname);

● i18nで使用するロケールの表の仕様はどうやって決まっているのですか?

i18nにおけるロケールの表は、主にISO 639言語コードとISO 3166国コードに基づいて決定されます。

ISO 639言語コードは、言語を識別するための2〜3文字のコードです。たとえば、英語のISO 639コードは "en" であり、日本語のISO 639コードは "ja" です。

一方、ISO 3166国コードは、国または地域を識別するための2文字または3文字のコードです。たとえば、アメリカ合衆国のISO 3166コードは "US" であり、日本のISO 3166コードは "JP" です。

これらのコードを組み合わせることで、i18nのロケールコードが形成されます。たとえば、英語を話すアメリカ人のためのロケールは、"en_US"と表記されます。同様に、日本語を話す日本人のためのロケールは、"ja_JP"と表記されます。

ただし、ISO規格以外のロケールコードも存在する場合があります。それらは、一般的な業界規格、アプリケーション固有の規則、あるいは地域の文化的および言語的な違いに基づくものがあります。
No.2289
03/28 14:05

edit

next.js で 現在のURLパスを取得する

● next.js で 現在のURLパスを取得する

・/docs/[id]/ で取得する場合

import { useRouter } from "next/router";
const { pathname } = useRouter()

・/docs/123456/ で取得する場合

import { useRouter } from "next/router";
const { asPath } = useRouter()
No.2244
03/15 15:13

edit

next.js の アプリをサブディレクトリで動作させる

● nginxの設定ファイル

      location /app/ {
          proxy_pass http://localhost:3000/;
      }

● next.config.js

assetPrefix を追加します

const SUB_DIRECTORY = "/app";

const isProduction = process.env.NODE_ENV === "production";

/** @type {import('next').NextConfig} */
const nextConfig = {
  assetPrefix: isProduction ? SUB_DIRECTORY : "/" ,
  reactStrictMode: true,
  swcMinify: true,
};

module.exports = nextConfig;
No.2213
03/09 13:12

edit

next.js の ルートURL を変更する

● Next.jsでURLのルートを変更する

next.config.js

module.exports = {
  assetPrefix: '/hoge'
};

● run dev / run build で自動的にセットされる NODE_ENV を使用して設定を分ける場合

next.config.js

const isProduction = process.env.NODE_ENV === "production";
module.exports = {
  assetPrefix: isProduction ? '/app' : '/'
};
No.2212
03/09 13:13

edit

next.js で cypress の E2Eテスト

● nextjsアプリと cypress のインストール

npx create-next-app@latest --ts cypress-testing-app
cd cypress-testing-app

npm install cypress --save-dev
npm install @testing-library/cypress --save-dev

package.json に以下を追加

  "scripts": {
    ......
    "cy:open": "cypress open",
    "cy:run": "cypress run"
  },

● nextjsアプリの実行

あらかじめ実行しておきます

npm run dev

● cypressの起動

最初に一度起動します

npm run cy:run

● テストスクリプトを追加

cypress\e2e\0-my-tests\0-my-sample.cy.js

describe('example to-do app', () => {

  it('ルートパスに訪問できるか', () => {
    cy.visit('http://localhost:3000/')
  })
  
})

● テストを実行(GUI)

npm run cy:open

● テストを実行(CUI)

npm run cy:run
No.2206
03/09 13:14

edit

nextjs middleware

middlewareはサーバサイドです

/src/middleware.ts

import { NextRequest, NextResponse } from 'next/server';

const isClient = () => typeof window !== 'undefined';

export const middleware = (req: NextRequest) => {
  console.log('isClient ?');
  console.log(isClient());
  return NextResponse.next();
};
No.2184
03/08 14:44

edit

Next.js で 画面遷移、1つ前の履歴に戻る

● 1. Link を使用する

import Link from 'next/link'

Next.js version 13以降

<Link href="/about">About Us</Link>

Next.js version 12以前

<Link href="/about"><a>About Us</a></Link>

● 2. onClick など、メソッドで画面遷移したい場合は 「useRouter」または「Router」を使用する

useRouter を使用する(こちらがおすすめです)

import { useRouter } from 'next/router';

const router = useRouter();

if (router.isReady) {
  router.push({
    pathname: '/login',
    query: { returnUrl: router.asPath }
  })
}

Router

import Router from 'next/router';

Router.push('/home');  // '/home'へ遷移

・useRouter と Router の違い

useRouter は hooks なので、 実際にルーターのインスタンス を取得したときにre-render されるので以下のコードが正しく実行できます。 Routerの場合は まだインスタンスがないので実行できません。

○ OK

import { useRouter } from "next/router";

  const router = useRouter();
  if (router.isReady) {
    router.push("/login");
  }

× NG

import Router from "next/router";

  if (Router.isReady) {
    Router.push("/login");
  }

● Next.js で 1つ前の履歴に戻る

      <button onClick={() => router.back()}>
        戻る
      </button>
No.2168
03/29 13:27

edit

Next.js + SQLite

nextjsアプリの初期化

npx create-next-app@latest --ts sample-app
cd sample-app

パッケージのインストール

npm install --save typescript ts-node
npm install --save typeorm sqlite3
npm install sqlite3 --save
npm install reflect-metadata --save
npm install @types/node --save
yarn typeorm init --database sqlite3

ormconfig.json が 自動生成されますので、以下のように追記します。

   "type": "sqlite",
   "database": "data/dev.sqlite",    
{
   "type": "sqlite",
   "database": "data/dev.sqlite",    

   "synchronize": true,
   "logging": false,
   "entities": [
      "src/entity/**/*.ts"
   ],
   "migrations": [
      "src/migration/**/*.ts"
   ],
   "subscribers": [
      "src/subscriber/**/*.ts"
   ],
   "cli": {
      "entitiesDir": "src/entity",
      "migrationsDir": "src/migration",
      "subscribersDir": "src/subscriber"
   }
}

テーブル Postを追加してみます

/src/entity/Post.ts

import {
  Entity,
  Column,
  PrimaryGeneratedColumn,
  CreateDateColumn,
  UpdateDateColumn,
} from 'typeorm';

@Entity()
export class Post {
  @PrimaryGeneratedColumn()
  readonly id: number;

  @Column('varchar', { length: 255, nullable: false })
  name: string;

  @Column('int', { nullable: true })
  sort_no: string;

  @CreateDateColumn()
  readonly created_at?: Date;

  @UpdateDateColumn()
  readonly updated_at?: Date;
}

マイグレーションの実行

Windowsのターミナルから

node_modules\.bin\ts-node ./node_modules/typeorm/cli.js  migration:generate -n Post
node_modules\.bin\ts-node ./node_modules/typeorm/cli.js  migration:run

と実行します

https://www.wakuwakubank.com/posts/730-typeorm-custom-naming/

elastomer_appま

●マイグレーション

yarn typeorm init

1. DB接続の確認 と マイグレーションの確認

yarn ts-node node_modules/.bin/typeorm migration:show

2. マイグレーションファイルの自動生成

エンティティ Post の マイグレーションファイルを自動生成する

yarn ts-node node_modules/.bin/typeorm migration:generate -n Post

src/migration/1647220932735-Post.ts といった命名のファイルが自動生成されます

3. マイグレーションの実行

yarn ts-node node_modules/.bin/typeorm migration:run
No.2162
03/09 13:17

edit

Next.js でdynamic import ( ssr: false ) による SSR回避

● Next.js で dynamic import ( ssr: false ) による SSR回避(その1)

・export default の場合

import MyComp from "../components/MyComp";

  ↓

import dynamic from "next/dynamic";

const MyCompNoSSR = dynamic(() => import("./MyComp"), { ssr: false });

以上で、SSRが回避されます。

・named export の場合

import MyComp from "../components/MyComp";

  ↓

import dynamic from "next/dynamic";

const MyCompNoSSR = dynamic(
  () => import("./MyComp").then((modules) => modules.MyComp),
  { ssr: false },
);

以上です。

● Next.js で dynamic import ( ssr: false ) による SSR回避(その2)

このように 呼び出されるコンポーネント側に記述することもできます

import App from '../components/App'

export default function About() {
  return (
    <App>
      <p>About Page</p>
    </App>
  )
}

  ↓

import dynamic from 'next/dynamic'
import App from '../components/App'

const About = ()=> {
  return (
    <App>
      <p>About Page</p>
    </App>
  )
}

export default dynamic(() => Promise.resolve(About), {
  ssr: false
})

以上で、SSRが回避されます。

● SSR回避の確認方法

ページをリロードしてhtmlソースを見てみます。

<p>About Page</p>

がなければ、SSRされていません。

オプション With no SSR

const DynamicComponentWithNoSSR = dynamic(
  () => import('../components/hello3'),
  { ssr: false }
)

オプション With suspense

React18以上が必要です。必ずバージョンを確認しましょう

const DynamicLazyComponent = dynamic(() => import('../components/hello4'), {
  suspense: true,
})

https://nextjs.org/docs/advanced-features/dynamic-import

No.2158
04/13 09:57

edit

Next.js アプリの初期化

● Next.js アプリの初期化(JavaScript)

npx create-next-app@latest  my-app

● Next.js アプリの初期化(TypeScript)

npx create-next-app@latest --ts my-app

NEXT JS アプリのビルドを高速化させるターボパックを追加するには with-turbopack オプションを追加します

npx create-next-app@latest  my-app  --ts with-turbopack

turbopack でビルドを行うには次のコマンドを実行します

next dev --turbo

● Next.js アプリの初期化(JavaScript + TailWind CSS を追加)

npx create-next-app -e with-tailwindcss my-project

-e オプションはこちらのリポジトリからデータを持ってきます https://github.com/vercel/next.js/tree/master/examples

● Next.js アプリの初期化(TypeScript + TailWind CSS を追加)

公式のリポジトリにサンプルがないためまずTypeScriptでアプリを作成してその後にTailWindを追加します

・1. Next.js アプリの初期化(TypeScript)

npx create-next-app@latest --ts my-app

・2. TailWindのインストールと設定ファイルの初期化

cd my-app
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest
npx tailwindcss init -p

・3. tailwind.config.js に mode と purge を追記する

module.exports = {
  mode: 'jit',
  purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
}

・4. tailwindを _app.ts から読み込む

import 'tailwindcss/tailwind.css';

・5. index.tsx の JSXを試しに以下のようにする

  return (
    <div className="text-red-500 text-4xl sm:text-6xl lg:text-7xl leading-none font-extrabold tracking-tight mt-10 mb-8 sm:mt-14 sm:mb-10">テストです</div>
  )

● VS Code の IntelliSense for CSS class names in HTML が重い場合

IntelliSense for CSS class names in HTML を無効にしましょう
No.2070
03/09 13:19

edit

Next. js で getLayout パターンで 共通のレイアウトページを作成する

● Next. js でgetLayout パターンで共通のレイアウトページを作成する

1. _app.tsx を変更する

変更前の _app.tsx

import '@/styles/globals.css'
import type { AppProps } from 'next/app'

export default function App({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />
}

 ↓ 変更後の _app.tsx

import "@/styles/globals.css";
import { ReactElement, ReactNode } from "react";
import { NextPage } from "next";
import type { AppProps } from "next/app";

type NextPageWithLayout = NextPage & {
  getLayout?: (page: ReactElement) => ReactNode;
};

type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout;
};

export default function App({ Component, pageProps }: AppPropsWithLayout) {
  const getLayout =
    Component.getLayout ||
    ((page) => {
      return page;
    });

  return getLayout(<Component {...pageProps} />);
}

2 . 共通レイアウト Layout.js の作成

メインの children のところが各ページ内容に置き換わります

mkdir components
vi components/Layout.js

components/Layout.jsx

import React from "react";

interface Props {
  children?: React.ReactNode;
}

const SimpleLayout: React.FC<Props> = ({ children }: Props) => {
  return (
    <>
      <h1>header</h1>
      {/* ===== メイン ===== */}
      <main>{children}</main>
      {/* ===== /メイン ===== */}
      <h1>footer</h1>
    </>
  );
};

export default SimpleLayout;

3 . レイアウトを各ページへ適用

変更前の src/pages/helloworld.tsx

const Helloworld = () => {
  return (
    <div>
      <h1>Helloworld</h1>
    </div>
  );
};
export default Helloworld;

  ↓  変更後の src/pages/helloworld.tsx

import SimpleLayout from "@/layouts/SimpleLayout";
import { ReactNode } from "react";

export default function Helloworld() {
  return (
    <div>
      <h1>ハローワールド</h1>
    </div>
  );
}

Helloworld.getLayout = (page: ReactNode) => {
  return <SimpleLayout>{page}</SimpleLayout>;
};
No.2119
03/07 18:05

edit

Next.js で 環境ごとに 設定ファイル(.env.development , .env.production)をわける

● Next.js の 環境設定ファイル(.env)

使うのは以下の2ファイルに限定すると良いでしょう。

.env.development : 開発用ファイル

NODE_ENV が development ( = npm run dev )の時に読み込まれる。

.env.production : 本番用ファイル

NODE_ENV が production ( = npm run build && npm run start )の時に読み込まれる。

● .envファイルの書き方と呼び出し方

書き方(サーバーサイド)
( .env.development または .env.production)

HOGE=mySettingValue

呼び出し方(サーバーサイド)
( xxx.js や xxx.ts ファイル )

console.log( process.env.HOGE );

書き方(フロントエンド)
( .env.development または .env.production)

NEXT_PUBLIC_HOGE=mySettingValue

呼び出し方(フロントエンド)
( xxx.js や xxx.ts ファイル )

console.log( process.env. NEXT_PUBLIC_HOGE );

● 現在「開発用」か「本番用」かどちらが読み込まれているのかをチェックする

NODE_ENV で判断すると良いでしょう

console.log( process.env.NODE_ENV );

● 現在「サーバーサイド」か「フロントエンド」かを判別する

typeof window === "undefined"

windowオブジェクトがないのがサーバーサイド
windowオブジェクトがあるのがフロントエンド
です。

デバッグ用にサーバーがクライアントを返したい場合はメソッドにしておいても良いかもです

export default function ServerOfClient() {
  return (typeof window === "undefined") ? 'Server' : 'Client';
}

Booleanの場合はそのまま利用しても良いですし以下のようにしても良いです

const isProduction = process.env.NODE_ENV === "production";

● envの値を確認するサンプル

  console.error(`● process.env.APOLLO_FETCH_POLICY は (${process.env.APOLLO_FETCH_POLICY}) / NODE_ENVは(${process.env.NODE_ENV}) / Server or Client は(${ServerOfClient()})`);

結果例

● process.env.APOLLO_FETCH_POLICY は (network-only-S) / NODE_ENVは(development) / Server or Client は(Server)

● .envファイルを書き換えたのに値が更新されない時は?

control + c でいちどプロセスを終了してから再度起動します
npm run dev
No.2088
05/15 09:22

edit

Next.js のコンポーネントが 「サーバー」/「クライアント」どちらで動いているかを調べる

● Next.js のコンポーネントが 「サーバー」/「クライアント」どちらで動いているかを調べる

引用 : https://maku.blog/p/m7is4dn/

windowオブジェクトはクライアント(WEBブラウザ)で動作している時のみ存在するのでこれを調べると言う方法です

const isClient = () => typeof window !== 'undefined'
// const isServer = () => typeof window === 'undefined'
const HelloPage: FC = () => {
  if (isClient()) {
    console.log('これはクライアントサイド JS として実行されているよ!')
    console.log(window.location)
    alert('だから alert も使えるよ!')
  }
  return <h1>Hello</h1>
}
No.2066
03/07 15:05

edit