diff --git a/app/layout.tsx b/app/layout.tsx
index 63e2178..e9bc5ad 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -1,6 +1,8 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
+import ChunkErrorBoundary from "@/components/layout/chunk-error-boundary";
+import "@/utils/chunk-error-handler";
const geistSans = Geist({
variable: "--font-geist-sans",
@@ -25,7 +27,9 @@ export default function RootLayout({
return (
- {children}
+
+ {children}
+
);
diff --git a/components/landing-page/latest-and-popular.tsx b/components/landing-page/latest-and-popular.tsx
index c3942b2..67bdd64 100644
--- a/components/landing-page/latest-and-popular.tsx
+++ b/components/landing-page/latest-and-popular.tsx
@@ -250,7 +250,11 @@ export default function LatestandPopular() {
{
+ constructor(props: Props) {
+ super(props);
+ this.state = { hasError: false };
+ }
+
+ static getDerivedStateFromError(error: Error): State {
+ // Check if it's a chunk loading error
+ if (error.name === 'ChunkLoadError' || error.message.includes('Loading chunk')) {
+ return { hasError: true, error };
+ }
+ return { hasError: false };
+ }
+
+ componentDidCatch(error: Error, errorInfo: ErrorInfo) {
+ console.error('Chunk loading error:', error, errorInfo);
+
+ // If it's a chunk loading error, try to reload the page
+ if (error.name === 'ChunkLoadError' || error.message.includes('Loading chunk')) {
+ this.setState({ hasError: true, error });
+ }
+ }
+
+ handleRetry = () => {
+ // Clear the error state and reload the page
+ this.setState({ hasError: false, error: undefined });
+ window.location.reload();
+ };
+
+ render() {
+ if (this.state.hasError) {
+ if (this.props.fallback) {
+ return this.props.fallback;
+ }
+
+ return (
+
+
+
+
+
+
+
+ Chunk Loading Error
+
+
+ There was an issue loading a part of the application. This usually happens when the application has been updated.
+
+
+
+
+
+
+
+
+
+ {process.env.NODE_ENV === 'development' && this.state.error && (
+
+
+ Error Details (Development)
+
+
+ {this.state.error.message}
+
+
+ )}
+
+
+ );
+ }
+
+ return this.props.children;
+ }
+}
+
+export default ChunkErrorBoundary;
\ No newline at end of file
diff --git a/components/main/dashboard/chart/column-chart.tsx b/components/main/dashboard/chart/column-chart.tsx
index 747bd02..d29575c 100644
--- a/components/main/dashboard/chart/column-chart.tsx
+++ b/components/main/dashboard/chart/column-chart.tsx
@@ -1,7 +1,7 @@
"use client";
import { getStatisticMonthly } from "@/service/article";
import React, { useEffect, useState } from "react";
-import dynamic from "next/dynamic";
+import { SafeReactApexChart } from "@/utils/dynamic-import";
type WeekData = {
week: number;
@@ -53,9 +53,6 @@ const ApexChartColumn = (props: {
const [seriesComment, setSeriesComment] = useState([]);
const [seriesView, setSeriesView] = useState([]);
const [seriesShare, setSeriesShare] = useState([]);
- const ReactApexChart = dynamic(() => import("react-apexcharts"), {
- ssr: false,
- });
useEffect(() => {
initFetch();
@@ -132,7 +129,7 @@ const ApexChartColumn = (props: {
return (
-
{
+ // Handle chunk loading errors
+ config.optimization = {
+ ...config.optimization,
+ splitChunks: {
+ ...config.optimization.splitChunks,
+ chunks: 'all',
+ cacheGroups: {
+ ...config.optimization.splitChunks?.cacheGroups,
+ vendor: {
+ test: /[\\/]node_modules[\\/]/,
+ name: 'vendors',
+ chunks: 'all',
+ },
+ // Separate CKEditor chunks
+ ckeditor: {
+ test: /[\\/]node_modules[\\/]@ckeditor[\\/]/,
+ name: 'ckeditor',
+ chunks: 'all',
+ priority: 20,
+ },
+ // Separate ApexCharts chunks
+ apexcharts: {
+ test: /[\\/]node_modules[\\/](apexcharts|react-apexcharts)[\\/]/,
+ name: 'apexcharts',
+ chunks: 'all',
+ priority: 20,
+ },
+ },
+ },
+ };
+
+ // Add error handling for chunk loading
+ if (!isServer) {
+ config.output = {
+ ...config.output,
+ chunkFilename: '[name].[chunkhash].js',
+ filename: '[name].[chunkhash].js',
+ };
+ }
+
+ return config;
+ },
+ // Add experimental features for better chunk handling
+ experimental: {
+ optimizePackageImports: ['@ckeditor/ckeditor5-react', 'react-apexcharts'],
+ },
};
export default nextConfig;
diff --git a/utils/chunk-error-handler.ts b/utils/chunk-error-handler.ts
new file mode 100644
index 0000000..fc09c39
--- /dev/null
+++ b/utils/chunk-error-handler.ts
@@ -0,0 +1,87 @@
+// Global chunk loading error handler
+export function setupChunkErrorHandler() {
+ if (typeof window === 'undefined') return;
+
+ // Handle chunk loading errors
+ window.addEventListener('error', (event) => {
+ const error = event.error;
+
+ // Check if it's a chunk loading error
+ if (
+ error?.name === 'ChunkLoadError' ||
+ error?.message?.includes('Loading chunk') ||
+ error?.message?.includes('Failed to fetch')
+ ) {
+ console.warn('Chunk loading error detected:', error);
+
+ // Prevent the error from being logged to console
+ event.preventDefault();
+
+ // Show a user-friendly message
+ const message = document.createElement('div');
+ message.style.cssText = `
+ position: fixed;
+ top: 20px;
+ right: 20px;
+ background: #fef2f2;
+ border: 1px solid #fecaca;
+ color: #dc2626;
+ padding: 12px 16px;
+ border-radius: 8px;
+ font-size: 14px;
+ z-index: 9999;
+ max-width: 300px;
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
+ `;
+ message.innerHTML = `
+
+ ⚠️
+ Application update detected. Please refresh the page.
+
+
+ `;
+
+ document.body.appendChild(message);
+
+ // Auto-remove after 10 seconds
+ setTimeout(() => {
+ if (message.parentNode) {
+ message.parentNode.removeChild(message);
+ }
+ }, 10000);
+ }
+ });
+
+ // Handle unhandled promise rejections (which might include chunk loading errors)
+ window.addEventListener('unhandledrejection', (event) => {
+ const error = event.reason;
+
+ if (
+ error?.name === 'ChunkLoadError' ||
+ error?.message?.includes('Loading chunk') ||
+ error?.message?.includes('Failed to fetch')
+ ) {
+ console.warn('Unhandled chunk loading rejection:', error);
+ event.preventDefault();
+
+ // Reload the page after a short delay
+ setTimeout(() => {
+ window.location.reload();
+ }, 1000);
+ }
+ });
+}
+
+// Auto-setup when this module is imported
+if (typeof window !== 'undefined') {
+ setupChunkErrorHandler();
+}
\ No newline at end of file
diff --git a/utils/dynamic-import.ts b/utils/dynamic-import.ts
new file mode 100644
index 0000000..2469892
--- /dev/null
+++ b/utils/dynamic-import.ts
@@ -0,0 +1,70 @@
+import dynamic from 'next/dynamic';
+import { ComponentType } from 'react';
+
+interface DynamicImportOptions {
+ ssr?: boolean;
+ loading?: () => React.ReactElement;
+ retries?: number;
+ retryDelay?: number;
+}
+
+export function createSafeDynamicImport>(
+ importFn: () => Promise<{ default: T }>,
+ options: DynamicImportOptions = {}
+) {
+ const { ssr = false, loading, retries = 3, retryDelay = 1000 } = options;
+
+ return dynamic(
+ () => {
+ return new Promise((resolve, reject) => {
+ let attempts = 0;
+
+ const attemptImport = async () => {
+ try {
+ const module = await importFn();
+ resolve(module.default);
+ } catch (error) {
+ attempts++;
+
+ // Check if it's a chunk loading error
+ if (
+ (error as any)?.name === 'ChunkLoadError' ||
+ (error as any)?.message?.includes('Loading chunk') ||
+ (error as any)?.message?.includes('Failed to fetch')
+ ) {
+ if (attempts < retries) {
+ console.warn(`Chunk loading failed, retrying... (${attempts}/${retries})`);
+ setTimeout(attemptImport, retryDelay);
+ return;
+ }
+ }
+
+ reject(error);
+ }
+ };
+
+ attemptImport();
+ });
+ },
+ {
+ ssr,
+ loading,
+ }
+ );
+}
+
+// Predefined safe dynamic imports for common components
+export const SafeCustomEditor = createSafeDynamicImport(
+ () => import('@/components/editor/custom-editor'),
+ { ssr: false }
+);
+
+export const SafeViewEditor = createSafeDynamicImport(
+ () => import('@/components/editor/view-editor'),
+ { ssr: false }
+);
+
+export const SafeReactApexChart = createSafeDynamicImport(
+ () => import('react-apexcharts'),
+ { ssr: false }
+);
\ No newline at end of file