Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

fullcalendar - NextJS - Dynamic import doesn't show calendar

I'm using NextJS and Fullcalendar.

I tried to use fullcalendar using dynamic import like in this example(for more info, this example solution comes from here).

It worked, But there was a problem. Almost every 1 out of 5 refreshing attempts ended in error Please import the top-level fullcalendar lib before attempting to import a plugin (like this, but versions are correct in my case)

After that, I found out that the modules option for next/dynamic has been deprecated. I thought it was a source of my problem (I'm not sure 100%, but at least it was deprecated and needed to be updated).

As docs says, newer way to handle dynamic imports is like this:

const DynamicComponent = dynamic(() =>
  import('../components/hello').then((mod) => mod.Hello)
)

But because we need multiple imports I found this solution.

For now, It seems like everything should be working, but my code has no effect.

import dynamic from "next/dynamic";
import { useEffect, useState } from "react";

import "@fullcalendar/common/main.css"; // @fullcalendar/react imports @fullcalendar/common
import "@fullcalendar/daygrid/main.css"; // @fullcalendar/timegrid imports @fullcalendar/daygrid
import "@fullcalendar/timegrid/main.css"; // @fullcalendar/timegrid is a direct import
import "./Fullcalendar.module.scss";
let CalendarComponent;

const Calendar = dynamic(() =>
  import("@fullcalendar/react").then((module) => module.Calendar)
);
const dayGridPlugin = dynamic(() =>
  import("@fullcalendar/daygrid").then((module) => module.dayGridPlugin)
);

export default function FullCalendar(props) {
  const [calendarLoaded, setCalendarLoaded] = useState(false);

  useEffect(() => {
    CalendarComponent = () => (
      <Calendar
        {...props}
        plugins={[dayGridPlugin]}
        initialView="dayGridMonth"
      />
    );
    setCalendarLoaded(true);
  }, []);

  let showCalendar = (props) => {
    if (!calendarLoaded) return <div>Loading ...</div>;
    return <CalendarComponent {...props} />;
  };

  return <div>{showCalendar(props)}</div>;
}

I also find another way to use next transpile modules. But they say that "there is currently no way to transpile only parts of a package, it's all or nothing".

In fact, I have something in next.config.js. Editing this file into transpile modules is another mysterious adventure, full of problems.

What should I have to do?

like image 329
Daniel Barbakadze Avatar asked Mar 22 '21 16:03

Daniel Barbakadze


2 Answers

I know the OP was asking for help making it work with a dynamic import, but in case anyone comes across this question trying to get FullCalendar to work with next.js in general (with or without dynamic importing) I hope this answer will help. My answer is a modification of @juliomalves' answer.

(1) Like @juliomalves and the official example, I installed next-transpile-modules, and included the appropriate dependencies

// next.config.js

const withTM = require('next-transpile-modules')([
  "@fullcalendar/common",
  "@fullcalendar/daygrid",
  "@fullcalendar/timegrid",
  "@fullcalendar/interaction",
  "@fullcalendar/react",
]);

module.exports = withTM({
  // any other next.js settings here
});

(2) Instead of modifying babel, I left it as is. Did not add a babel.config.js, or the babel-plugin-transform-require-ignore plugin.

(3) I added the appropriate CSS in the same way as the official example.

// _app.tsx

import '@fullcalendar/common/main.css'
import '@fullcalendar/daygrid/main.css'
import '@fullcalendar/timegrid/main.css'

(4) I imported the FullCalendar dependencies in my custom component.

// components/FullCalendar.tsx

import Calendar from '@fullcalendar/react';
import dayGridPlugin from '@fullcalendar/daygrid';
import styles from './Fullcalendar.module.scss';

export default function FullCalendar(props) {
    return (
       <Calendar {...props} plugins={[dayGridPlugin]} initialView="dayGridMonth" />
    );
}

(5) And then instead of importing dynamically, I imported regularly into the page where I needed the calendar.

like image 127
Levon Avatar answered Sep 20 '22 16:09

Levon


Making FullCalendar work properly with Next.js requires some initial setup and a few changes to your initial code.

First, let's install next-transpile-modules to process fullcalendar's ES modules. Make sure to include all @fullcalendar dependencies you use.

// next.config.js

const withTM = require('next-transpile-modules')([
  '@fullcalendar/common',
  '@fullcalendar/react',
  '@fullcalendar/daygrid'
]);

module.exports = withTM({
  // any other next.js settings here
});

Next, add a custom Babel configuration to ignore CSS imports used in fullcalendar - babel-plugin-transform-require-ignore plugin will need to be installed. This prevents the Global CSS cannot be imported from within node_modules error from Next.js.

// babel.config.js

module.exports = {
    presets: [
        '@babel/preset-react'
    ],
    overrides: [
        {
            include: ['./node_modules'],
            plugins: [
                [
                    'babel-plugin-transform-require-ignore',
                    {
                        extensions: ['.css']
                    }
                 ]
            ]
        }
    ]
};

You'll have to include fullcalendar's global CSS in _app.js to compensate for this. Global CSS can only be imported from this file in Next.js.

// _app.js

import '@fullcalendar/common/main.css'
import '@fullcalendar/daygrid/main.css'
import '@fullcalendar/timegrid/main.css'

Finally, you can simplify and refactor your FullCalendar component. Here you don't have to dynamically import its fullcalendar dependencies, we'll dynamically import the component itself when using it.

// components/FullCalendar.jsx

import Calendar from '@fullcalendar/react';
import dayGridPlugin from '@fullcalendar/daygrid';
import styles from './Fullcalendar.module.scss';

export default function FullCalendar(props) {
    return (
       <Calendar {...props} plugins={[dayGridPlugin]} initialView="dayGridMonth" />
    );
}

Then, dynamically import your custom component wherever it's used.

import dynamic from 'next/dynamic';

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

const SomePage = () => {
    return <FullCalendar />;
}

export default SomePage;

For reference, this is based on the official example from fullcalendar, with some adaptations to make it work with next-transiple-modules v6.4.0.

like image 25
juliomalves Avatar answered Sep 22 '22 16:09

juliomalves