---
# System prepended metadata

title: Next.js 整合 Tailwind CSS、CKEditor 4 和 Bootstrap 4.6 專案指南
tags: [tailwind, CKeditor, nextjs, Bootstrap]

---

# Next.js 整合 Tailwind CSS、CKEditor 4 和 Bootstrap 4.6 專案指南

###### tags: `nextjs`, `tailwind` , `CKeditor`, `Bootstrap`

## 專案概述

本專案成功整合了 Next.js、Tailwind CSS、CKEditor 4 和 Bootstrap 4.6，建立了一個編輯器頁面，使用者可以在 CKEditor 中編輯內容並即時預覽結果。我們還創建了一個使用 Tailwind CSS 的 Header 組件，展示了響應式設計和過渡效果。

## 完整步驟指南

### 1. 建立 Next.js 專案

```bash
npx create-next-app@latest nextjs-tailwind-ckeditor-bootstrap4
```

選擇以下選項：
- 使用 TypeScript：是
- 使用 ESLint：是
- 使用 Tailwind CSS：是
- 使用 `src/` 目錄：是
- 使用 App Router：是
- 自定義導入別名：是（預設 `@/*`）

### 2. 安裝必要依賴

由於 React 19 與 ckeditor4-react 的相容性問題，需要使用 `--legacy-peer-deps` 參數：

```bash
cd nextjs-tailwind-ckeditor-bootstrap4
npm install ckeditor4-react@4.2.0 bootstrap@4.6.0 jquery --legacy-peer-deps
```

> 注意：我們選擇 ckeditor4-react@4.2.0，它使用的是 CKEditor 4.22.1，這是最後一個不需要商業許可證的版本。

### 3. 建立 Bootstrap CSS 檔案

在 `public` 目錄下創建 `bootstrap.min.css` 文件，通過 CDN 引用 Bootstrap 4.6：

```css
/* 從 CDN 引入 Bootstrap 4.6 CSS */
@import url('https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css');
```

### 4. 建立首頁

修改 `src/app/page.tsx`：

```tsx
import Link from 'next/link';

export default function Home() {
  return (
    <main className="min-h-screen p-8">
      <h1 className="text-3xl font-bold mb-6">Next.js 整合 Tailwind CSS 與 CKEditor 4</h1>
      
      <div className="max-w-2xl">
        <p className="mb-4 text-gray-700">
          這是一個示範專案，展示如何在 Next.js 中整合 Tailwind CSS、CKEditor 4 和 Bootstrap 4.6。
        </p>
        
        <div className="bg-blue-50 border-l-4 border-blue-500 p-4 mb-6">
          <h2 className="text-lg font-semibold text-blue-800">專案特點</h2>
          <ul className="list-disc ml-5 mt-2 text-blue-700">
            <li>使用 Next.js 最新版本和 App Router</li>
            <li>整合 Tailwind CSS 進行現代化 UI 設計</li>
            <li>使用 CKEditor 4 作為富文本編輯器</li>
            <li>整合 Bootstrap 4.6 樣式到 CKEditor 中</li>
            <li>實現編輯內容的即時預覽功能</li>
          </ul>
        </div>
        
        <Link 
          href="/editor" 
          className="inline-block bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-lg transition-colors duration-300"
        >
          前往編輯器頁面
        </Link>
      </div>
    </main>
  );
}
```

### 5. 建立 Header 組件

在 `src/components` 目錄下創建 `Header.tsx`：

```tsx
import Link from 'next/link';
import { useState } from 'react';

export default function Header() {
  const [isMenuOpen, setIsMenuOpen] = useState(false);

  return (
    <header className="bg-gradient-to-r from-blue-600 to-blue-800 text-white shadow-lg">
      <div className="container mx-auto px-4 py-3">
        <div className="flex justify-between items-center">
          <div className="flex items-center space-x-2">
            <svg 
              className="h-8 w-8" 
              xmlns="http://www.w3.org/2000/svg" 
              fill="none" 
              viewBox="0 0 24 24" 
              stroke="currentColor"
            >
              <path 
                strokeLinecap="round" 
                strokeLinejoin="round" 
                strokeWidth={2} 
                d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" 
              />
            </svg>
            <span className="font-bold text-xl">編輯器測試專案</span>
          </div>

          {/* 桌面導航 */}
          <nav className="hidden md:flex space-x-6">
            <Link href="/" className="hover:text-blue-200 transition duration-300">
              首頁
            </Link>
            <Link href="/editor" className="hover:text-blue-200 transition duration-300">
              編輯器
            </Link>
            <a href="#" className="hover:text-blue-200 transition duration-300">
              關於
            </a>
            <a href="#" className="hover:text-blue-200 transition duration-300">
              聯絡我們
            </a>
          </nav>

          {/* 行動裝置選單按鈕 */}
          <div className="md:hidden">
            <button 
              onClick={() => setIsMenuOpen(!isMenuOpen)}
              className="focus:outline-none"
            >
              <svg 
                className="h-6 w-6" 
                xmlns="http://www.w3.org/2000/svg" 
                fill="none" 
                viewBox="0 0 24 24" 
                stroke="currentColor"
              >
                {isMenuOpen ? (
                  <path 
                    strokeLinecap="round" 
                    strokeLinejoin="round" 
                    strokeWidth={2} 
                    d="M6 18L18 6M6 6l12 12" 
                  />
                ) : (
                  <path 
                    strokeLinecap="round" 
                    strokeLinejoin="round" 
                    strokeWidth={2} 
                    d="M4 6h16M4 12h16M4 18h16" 
                  />
                )}
              </svg>
            </button>
          </div>
        </div>

        {/* 行動裝置選單 */}
        {isMenuOpen && (
          <div className="md:hidden mt-4 pb-2">
            <Link 
              href="/" 
              className="block py-2 hover:bg-blue-700 px-2 rounded transition duration-300"
              onClick={() => setIsMenuOpen(false)}
            >
              首頁
            </Link>
            <Link 
              href="/editor" 
              className="block py-2 hover:bg-blue-700 px-2 rounded transition duration-300"
              onClick={() => setIsMenuOpen(false)}
            >
              編輯器
            </Link>
            <a 
              href="#" 
              className="block py-2 hover:bg-blue-700 px-2 rounded transition duration-300"
              onClick={() => setIsMenuOpen(false)}
            >
              關於
            </a>
            <a 
              href="#" 
              className="block py-2 hover:bg-blue-700 px-2 rounded transition duration-300"
              onClick={() => setIsMenuOpen(false)}
            >
              聯絡我們
            </a>
          </div>
        )}
      </div>
    </header>
  );
}
```

### 6. 建立編輯器頁面

創建 `src/app/editor/page.tsx`：

```tsx
'use client';

import { useState, useEffect } from 'react';
import Link from 'next/link';
import dynamic from 'next/dynamic';
import Header from '@/components/Header';

// 動態導入 CKEditor 組件，避免服務器端渲染問題
const CKEditor = dynamic(
  async () => {
    // 在客戶端導入 Bootstrap 和 CKEditor
    // 使用 await import() 而不是直接 import 語句
    if (typeof window !== 'undefined') {
      await import('bootstrap/dist/css/bootstrap.min.css');
      
      const mod = await import('ckeditor4-react');
      
      // 確保 jQuery 和 Bootstrap JS 只在客戶端加載
      window.jQuery = require('jquery');
      window.$ = window.jQuery;
      require('bootstrap/dist/js/bootstrap.bundle.min.js');
      
      // 全局設置 CKEditor 配置，禁用版本檢查
      if (window.CKEDITOR) {
        window.CKEDITOR.disableAutoInline = true;
        window.CKEDITOR.config.versionCheck = false;
      }
      
      return mod.CKEditor;
    }
    return null;
  },
  { 
    ssr: false,  // 禁用服務器端渲染
    loading: () => <p className="text-gray-500">CKEditor 加載中...</p>
  }
);

export default function EditorPage() {
  const [content, setContent] = useState('<p>這裡將顯示編輯器內容</p>');
  const [editorLoaded, setEditorLoaded] = useState(false);

  // 確保組件只在客戶端渲染
  useEffect(() => {
    setEditorLoaded(true);
  }, []);

  // 處理編輯器內容變更
  const handleEditorChange = (evt) => {
    const data = evt.editor.getData();
    setContent(data);
  };

  return (
    <>
      <Header />
      <main className="min-h-screen p-8">
        <h1 className="text-3xl font-bold mb-6">CKEditor 整合頁面</h1>
        
        <div className="mb-8">
          <Link href="/" className="text-blue-500 hover:text-blue-700 underline">
            返回首頁
          </Link>
        </div>
        
        <div className="grid grid-cols-1 gap-8 md:grid-cols-2">
          <div className="border border-gray-300 p-4 rounded-lg shadow-md hover:shadow-lg transition-shadow duration-300">
            <h2 className="text-xl font-semibold mb-4">編輯器</h2>
            <div className="min-h-[300px]">
              {editorLoaded && (
                <CKEditor
                  config={{
                    // 禁用版本檢查警告
                    versionCheck: false,
                    // 啟用 Bootstrap 內容模板
                    contentsCss: '/bootstrap.min.css',
                    // 允許使用 Bootstrap 類別
                    allowedContent: true,
                    // 添加 Bootstrap 相關按鈕和功能
                    extraPlugins: 'div,autolink,autoembed,embedsemantic,autogrow,widget,lineutils,clipboard,dialog,dialogui,elementspath',
                    toolbar: [
                      { name: 'document', items: [ 'Source' ] },
                      { name: 'clipboard', items: [ 'Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo' ] },
                      { name: 'basicstyles', items: [ 'Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'RemoveFormat' ] },
                      { name: 'paragraph', items: [ 'NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote', 'CreateDiv', '-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock' ] },
                      { name: 'links', items: [ 'Link', 'Unlink' ] },
                      { name: 'insert', items: [ 'Image', 'Table', 'HorizontalRule', 'SpecialChar' ] },
                      { name: 'styles', items: [ 'Styles', 'Format', 'Font', 'FontSize' ] },
                      { name: 'colors', items: [ 'TextColor', 'BGColor' ] },
                      { name: 'tools', items: [ 'Maximize', 'ShowBlocks' ] },
                    ],
                    // 添加 Bootstrap 相關樣式
                    stylesSet: [
                      { name: 'Alert Primary', element: 'div', attributes: { 'class': 'alert alert-primary' } },
                      { name: 'Alert Success', element: 'div', attributes: { 'class': 'alert alert-success' } },
                      { name: 'Alert Danger', element: 'div', attributes: { 'class': 'alert alert-danger' } },
                      { name: 'Button Primary', element: 'a', attributes: { 'class': 'btn btn-primary' } },
                      { name: 'Button Success', element: 'a', attributes: { 'class': 'btn btn-success' } },
                      { name: 'Button Danger', element: 'a', attributes: { 'class': 'btn btn-danger' } },
                      { name: 'Card', element: 'div', attributes: { 'class': 'card' } },
                      { name: 'Card Body', element: 'div', attributes: { 'class': 'card-body' } },
                      { name: 'Card Header', element: 'div', attributes: { 'class': 'card-header' } },
                      { name: 'Card Footer', element: 'div', attributes: { 'class': 'card-footer' } },
                    ]
                  }}
                  data={content}
                  onChange={handleEditorChange}
                />
              )}
            </div>
          </div>
          
          <div className="border border-gray-300 p-4 rounded-lg shadow-md hover:shadow-lg transition-shadow duration-300">
            <h2 className="text-xl font-semibold mb-4">預覽結果</h2>
            <div 
              className="min-h-[300px] bg-white p-4 rounded border border-gray-200 overflow-auto"
              dangerouslySetInnerHTML={{ __html: content }}
            />
          </div>
        </div>
      </main>
    </>
  );
}
```

### 7. 啟動開發伺服器

```bash
npm run dev
```

## 主要挑戰與解決方案

### 1. React 19 與 ckeditor4-react 的相容性問題

**問題**：ckeditor4-react 依賴較舊版本的 React，與 Next.js 15 使用的 React 19 不相容。
**解決方案**：使用 `--legacy-peer-deps` 參數安裝依賴。

### 2. 避免服務器端渲染 (SSR) 問題

**問題**：CKEditor 是客戶端庫，在服務器端渲染時會出錯。
**解決方案**：使用 Next.js 的動態導入 (`next/dynamic`) 並設置 `ssr: false`。

### 3. CKEditor 4 許可證問題

**問題**：CKEditor 4.23.0 及以上版本需要商業許可證。
**解決方案**：使用最後一個免費版本 4.22.1 (通過 ckeditor4-react@4.2.0)。

### 4. 版本檢查警告

**問題**：CKEditor 4.22.1 會顯示版本不安全的警告。
**解決方案**：
1. 在組件配置中設置 `versionCheck: false`
2. 全局設置 `window.CKEDITOR.config.versionCheck = false`

### 5. Bootstrap 樣式整合

**問題**：需要在 CKEditor 中使用 Bootstrap 4.6 樣式。
**解決方案**：
1. 在 `public` 目錄下創建 `bootstrap.min.css`
2. 在 CKEditor 配置中設置 `contentsCss: '/bootstrap.min.css'`
3. 添加 Bootstrap 相關樣式到 `stylesSet` 配置中

## 提示 (Prompt) 範例

以下是一些可用於創建類似專案的提示範例：

### 建立 Next.js 與 CKEditor 4 整合專案

```
建立一個 Next.js 專案，整合 Tailwind CSS、CKEditor 4 和 Bootstrap 4.6。專案需要：
1. 使用 App Router 架構
2. 在編輯器頁面整合 CKEditor 4 和 Bootstrap 4.6
3. 使用動態導入避免 SSR 問題
4. 實現編輯內容的即時預覽功能
5. 使用 Tailwind CSS 設計現代化 UI
6. 創建響應式 Header 組件
```

### 解決 CKEditor 4 在 Next.js 中的整合問題

```
我正在 Next.js 15 (React 19) 專案中整合 CKEditor 4，遇到以下問題，請提供解決方案：
1. React 版本衝突問題
2. 服務器端渲染錯誤
3. CKEditor 4 版本警告訊息
4. 如何在 CKEditor 中使用 Bootstrap 4.6 樣式
5. 如何實現編輯內容的即時預覽
```

### 優化 CKEditor 4 與 Bootstrap 4.6 整合

```
我已經在 Next.js 專案中整合了 CKEditor 4 和 Bootstrap 4.6，請提供以下優化建議：
1. 如何添加更多 Bootstrap 元素到 CKEditor 工具列
2. 如何自定義 CKEditor 的外觀以匹配 Tailwind CSS 設計
3. 如何處理 CKEditor 的版本警告
4. 如何提升編輯器的加載性能
5. 如何實現更豐富的即時預覽功能
```
