FullCalendarは、月・週・日・リスト表示とドラッグ&ドロップによる予定変更を備えた、JavaScriptのイベントカレンダーライブラリです。2026年6月19日にv7.0.0、9月5日にv7.1.0が公開され、npm install fullcalendar で入るのはv7になりました。v7はパッケージ構成とCSSの読み込み方が変わったため、v6時代の解説記事のコードを写すと動きません。この記事では、v7.1での導入手順、日本語化とタイムゾーン、イベントの追加・編集とデータベース連携、無料で使える範囲と商用ライセンス、v6からの移行でつまずく箇所を、実際にnpmで入れて動かした結果とあわせて解説します。
まとめ:FullCalendar v7.1を使う前に押さえる要点
- 現行版は7.1.0(2026年9月5日)。本体は
fullcalendarパッケージ1つにまとまり、月表示などのプラグインはfullcalendar/daygridのようなサブパスから読み込みます - v7ではJavaScriptからCSSが自動注入されません。
skeleton.cssとテーマのCSS2枚を自分で読み込み、temporal-polyfillも一緒にインストールします - 標準機能はMITライセンスで商用利用も無料です。タイムライン表示やリソース表示はPremiumで、商用の非公開システムでは480ドルからの有償ライセンスが必要です
- 日本語化は
fullcalendar/locales/jaを渡すだけで、timeZone: 'Asia/Tokyo'のような名前付きタイムゾーンもプラグインなしで使えます - v6の記事どおり
@fullcalendar/daygridを入れるとv6.1.21が入り、v7本体と混ざって動きません
以下の例はFullCalendar 7.1.0を対象としています。jsdomによる日本語表示、Asia/TokyoのJSONフィード、予定の追加・削除と、v6プラグイン混在時のエラーを検証しています。
FullCalendarの概要と現行版7.1の位置づけ
FullCalendarはAdam Shaw氏が開発するオープンソースのカレンダーUIで、公式サイトは fullcalendar.io、ソースはGitHubの fullcalendar/fullcalendar で公開されています。予約システム、会議室や設備の空き状況、シフト表、社内のスケジュール共有など、「予定を日付の枠に並べて、クリックやドラッグで操作する」画面を短期間で作る用途で使われます。
v7はテーマの仕組みを作り直したメジャーバージョンです。公式の変更履歴では、正式なテーマシステムと4つの新テーマ(Monarch・Forma・Breezy・Pulse)の追加、DOM構造の簡素化、アクセシビリティと印刷表示の改善が挙げられ、少なくとも57件のチケットが解決されています。v7.1.0では、月表示で dayMaxEvents: true を使うと、必要なイベントだけをDOMへ挿入するよう描画量が減りました。その結果、eventContent などの描画フックが一部のイベントにしか呼ばれなくなる破壊的変更も含まれています。
| 版 | 公開日 | npmでの扱い(2026年9月27日時点) |
|---|---|---|
| 7.1.0 | 2026-09-05 | latest(既定でインストールされる) |
| 7.0.0 | 2026-06-19 | 公開当初は @7 指定が必要だった |
| 6.1.21 | 2026-06-18 | 確認時点のv6系最新リリース |
v7.0.0の公開時点ではnpmのlatestタグがv6のままで、公式のリリースノートも @fullcalendar/react@7 のように @7 を付けて入れるよう案内していました。現在はlatestが7.1.0に移ったため、バージョンを指定せずに入れるとv7が入ります。
なお、WordPressの「WP FullCalendar」やObsidianの「Full Calendar」は、このライブラリを組み込んだ別の製品です。WP FullCalendarの説明文はFullCalendar 3.xを使うと明記しており、ObsidianのプラグインはFullCalendar 5.x系に依存しており、GitHubのリポジトリは現在community-archive配下で、2026年8月1日付でアーカイブ(読み取り専用)になっています。どちらもこの記事のv7の手順は当てはまりません。
無料で使える範囲と商用利用のライセンス
月・週・日・リスト・複数月の表示、ドラッグ&ドロップ、日本語化など、ドキュメントで「Premium」と印の付いていない機能は、すべてMITライセンスです。MITは著作権表示とライセンス文を残せば、社内システムや受託開発、有料のSaaSを含めて無償で使えます。
有償になるのは、横軸に時間を取るタイムライン表示や、会議室・担当者などの「リソース」ごとに列や行を分ける表示です。これらはPremium(パッケージ名は fullcalendar-scheduler)に含まれ、ライセンスは次の3つから選びます。
| 利用形態 | 選ぶライセンス | schedulerLicenseKey |
|---|---|---|
| 非公開・営利(一般的な業務システム) | 商用ライセンス(480ドルから) | 購入時に発行されるキー |
| 登録非営利団体の非商用利用、または評価目的 | CC BY-NC-ND 4.0(政府機関・大学は非商用枠の対象外) | CC-Attribution-NonCommercial-NoDerivatives |
| ソース公開のプロジェクト | AGPLv3(v6まではGPLv3) | AGPL-My-Frontend-And-Backend-Are-Open-Source |
v7で見落としやすいのが、オープンソース向けの選択肢がGPLv3からAGPLv3に変わった点です。公式は、GPLv3のままだとソースを配布しないSaaSが「GPL準拠」を名乗って無償で使えてしまう抜け道があったため、それを塞ぐ目的だと説明しています。AGPLv3を選べるのはフロントエンドとバックエンドの両方をAGPLv3で公開できる場合に限られるので、非公開の業務システムでPremium機能を使うなら商用ライセンスを買うことになります。
商用ライセンスの価格表示は「Starting at $480」で、席数は「Premiumのソース・JavaScript API・CSSに触れる開発者」の人数で数えます。有効期限前に更新すれば基本価格の50%引き、期限後なら25%引きで、更新をやめても期限切れ前に出た最終版は使い続けられます。評価だけならCC BY-NC-NDのキーで期間の制限なく試せますが、商用の本番環境では使えません。
v7のインストール手順:npmとCDN
npmで入れる手順(Vite・webpackなどのビルド環境)
本体と temporal-polyfill を一緒に入れます。temporal-polyfill はv7で全パッケージのpeerDependencyになりました。FullCalendarは内部で使うだけでグローバルには登録しないため、アプリ全体でTemporal APIを使いたい場合は import 'temporal-polyfill/global' を自分で書きます。
npm install [email protected] temporal-polyfill
v7では、テーマもプラグインとして読み込みます。CSSは skeleton.css(必須)、テーマの theme.css、配色の palette.css の3枚です。v6と同じ見た目にしたい場合は classic テーマを選びます。
import { Calendar } from 'fullcalendar'
import dayGridPlugin from 'fullcalendar/daygrid'
import classicThemePlugin from 'fullcalendar/themes/classic'
import 'fullcalendar/skeleton.css'
import 'fullcalendar/themes/classic/theme.css'
import 'fullcalendar/themes/classic/palette.css'
const calendar = new Calendar(document.getElementById('calendar'), {
plugins: [classicThemePlugin, dayGridPlugin],
initialView: 'dayGridMonth',
events: [{ title: '定例会議', start: '2026-10-05T10:00:00' }],
})
calendar.render()
v7のパッケージはESMだけで配布され、CommonJS版はなくなりました。公式の移行ガイドはNode.js 22.12以降と23以降なら require('fullcalendar') がそのまま動くとしており、Node.js 20系でも20.19.0以降は require() で同期ESMを読み込めます。ただし、トップレベルawaitを含むモジュールやJest独自の読み込み処理には別の制約があるため、テスト環境のESM対応を確認してください。ESMとCommonJSの違いはNative ESMとCommonJSの違いを整理した記事で解説しています。
CDNのscriptタグで入れる手順
ビルド環境を使わない場合は、jsDelivrから読み込みます。v7ではファイル名が変わり、v6の index.global.min.js は all/global.js に、ロケールは locales/ja/global.js の形になりました。all/global.js には interaction・daygrid・timegrid・list・multimonth の各プラグインが入っています。
<script src="https://cdn.jsdelivr.net/npm/[email protected]/all/global.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/themes/classic/global.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/locales/ja/global.js"></script>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/skeleton.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/themes/classic/theme.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/themes/classic/palette.css" rel="stylesheet">
<script>
document.addEventListener('DOMContentLoaded', function () {
var calendar = new FullCalendar.Calendar(document.getElementById('calendar'), {
initialView: 'dayGridMonth',
locale: 'ja',
})
calendar.render()
})
</script>
<div id="calendar"></div>
URLのバージョンは @7.1.0 のように固定してください。@7 や無指定にすると、次のリリースで表示が変わっても気づけません。
v6の記事のコードがv7で動かない原因
古いFullCalendarの日本語記事を参照するときは、掲載コードの対象バージョンを確認してください。v6では本体が @fullcalendar/core、月表示が @fullcalendar/daygrid という別パッケージでした。v7ではこれらが fullcalendar のサブパスに統合されました。ところが @fullcalendar/daygrid などの旧プラグインはnpmに残っており、latestは6.1.21を指したままです。@fullcalendar/core は7.1.0が出ていますが、v7では型定義用のパッケージになり、直接importすると「@fullcalendar/core should not be imported directly」と表示されて Calendar は取り出せません。そのため、v6の記事どおりに入れると、エラーにならずにv6とv7が混ざることがあります。
2026年9月27日に空のプロジェクトで試した結果は次のとおりです。
| 実行したコマンド・操作 | 結果 |
|---|---|
npm i @fullcalendar/react @fullcalendar/daygrid |
react 7.1.0とdaygrid 6.1.21が入り、coreも2版(7.1.0と6.1.21)入る。警告なし |
@fullcalendar/[email protected] 導入済みでdaygrid 6.1.21を追加 |
ERESOLVE で停止(daygridが @fullcalendar/core@"~6.1.21" を要求) |
| v7本体にv6のdaygridプラグインを渡して描画 | 描画時にTypeErrorで停止 |
3つ目のエラーメッセージは TypeError: Class constructor DayTableView cannot be invoked without 'new' でした。最初のケースはインストールが通るぶん、原因に気づきにくいパターンです。v7に移るときは、package.jsonから @fullcalendar/core・@fullcalendar/daygrid・@fullcalendar/timegrid・@fullcalendar/list・@fullcalendar/interaction を消し、import文を fullcalendar/daygrid(Reactなら @fullcalendar/react/daygrid)に書き換えます。Premiumの @fullcalendar/resource-timeline なども同様に、Vanilla JSでは fullcalendar-scheduler/resource-timeline、Reactでは @fullcalendar/react-scheduler に移っています。
パッケージ以外でv7の移行時に書き換えが必要になる主な項目は次の4つです。
- CSS:v6は自動で注入されていたが、v7では
skeleton.cssとテーマのCSSを明示的に読み込む - イベントの色:
backgroundColor・borderColorはcolorに、textColorはcontrastColorに、classNamesはclassNameに改名。カレンダー全体のeventBackgroundColorもeventColorに変わった - タイムゾーン:
@fullcalendar/luxon3と@fullcalendar/moment-timezoneは廃止。名前付きタイムゾーンは本体だけで扱える - Vue 2:
@fullcalendar/vue(Vue 2用)はv7で提供終了。Vue 2のまま使うならv6に留まる
色の設定名が変わっても、旧名を書いたままではエラーになりません。色が付かないときは、まずここを疑ってください。
基本の使い方:ビュー切り替えとイベント表示
実務でよく使う設定をまとめると、次のようになります。月・週・リストの切り替えボタン、営業時間の網掛け、1日に入りきらない予定の「他 N 件」表示を入れた例です。先ほどのCSS3枚と、idがcalendarの描画先要素を用意した状態で実行します。
import { Calendar } from 'fullcalendar'
import dayGridPlugin from 'fullcalendar/daygrid'
import timeGridPlugin from 'fullcalendar/timegrid'
import listPlugin from 'fullcalendar/list'
import interactionPlugin from 'fullcalendar/interaction'
import classicThemePlugin from 'fullcalendar/themes/classic'
import jaLocale from 'fullcalendar/locales/ja'
const calendar = new Calendar(document.getElementById('calendar'), {
plugins: [classicThemePlugin, dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin],
locale: jaLocale,
timeZone: 'Asia/Tokyo',
initialView: 'dayGridMonth',
initialDate: '2026-10-01',
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,listWeek',
},
businessHours: { daysOfWeek: [1, 2, 3, 4, 5], startTime: '09:00', endTime: '18:00' },
dayMaxEvents: true,
eventColor: '#2563eb',
events: '/api/events',
})
calendar.render()
initialView に指定できるのは、読み込んだプラグインが提供するビューだけです。dayGridMonth はdaygrid、timeGridWeek はtimegrid、listWeek はlistが必要で、年間表示の multiMonthYear を使うなら fullcalendar/multimonth を追加します。headerToolbar の right に並べたビュー名が、そのまま切り替えボタンになります。
見た目の調整は、v7ではまずテーマとパレットを選び、それで足りない部分だけCSSやclassName系の設定で上書きする順番になります。テーマは公式の themes.fullcalendar.io で比較でき、全テーマがダークモードに対応しています。v7の変更履歴では、Tailwind CSSと組み合わせやすくなったことも挙げられています。
日本語化とタイムゾーンの設定
日本語化(locale: ja)
npmで入れた場合は fullcalendar/locales/ja をimportして locale に渡し、CDNの場合は locales/ja/global.js を読み込んでから locale: 'ja' を指定します。v7.1.0同梱の日本語ロケールでは、ボタンが「前」「次」「今日」「月」「週」「日」「予定リスト」、終日欄が「終日」、あふれた予定が「他 N 件」と表示されます。上のコードをjsdomで描画すると、10月の月表示のタイトルは「2026年10月」になりました。
日本語ロケールには週の開始曜日の指定が入っていないため、既定の日曜始まりになります。月曜始まりにしたい場合は firstDay: 1 を追加してください。
名前付きタイムゾーン(timeZone)
timeZone の既定値は 'local'(閲覧者のブラウザの時刻)です。v7では 'Asia/Tokyo' のようなIANAのタイムゾーン名を、追加プラグインなしで指定できるようになりました。v6では名前付きタイムゾーンにLuxonかmoment-timezoneの連携プラグインが必要でしたが、v7ではpeerDependencyとしてインストールした temporal-polyfill を内部で使って解決します。
注意点は、コールバックやAPIが返す日付がTemporalではなく従来のJavaScriptの Date のままであることです。公式ドキュメントも、返ってくる Date が実質的に扱えるのはローカルとUTCだけだとしています。Asia/Tokyo を指定していても、getHours() は閲覧者のPCのタイムゾーンで値を返すため、サーバーに送る値は startStr(オフセット付きのISO 8601文字列)を使うのが安全です。
日本の祝日を表示する方法
FullCalendarには祝日のデータが入っていません。内閣府が公開している 国民の祝日CSV(2026年9月27日時点で1955年から2027年11月23日まで収録・Shift_JIS)をサーバー側で読み込み、背景イベントとして返すのが手軽です。
{ "title": "スポーツの日", "start": "2026-10-12", "allDay": true, "display": "background", "color": "#fde2e2" }
display: 'background' を付けると、予定の下に色の帯として描画されます。CSVには翌年分までしか入っていないため、年に1回はサーバー側で取り直す処理を入れておくと更新漏れを防げます。
クリック・ドラッグによるイベントの追加・編集・削除
日付のクリック(dateClick)、範囲選択(select)、予定のドラッグ移動(eventDrop)、長さの変更(eventResize)には fullcalendar/interaction が必要です。予定のクリック(eventClick)だけならinteractionなしで動きます。dateClick が反応しない場合は、まずinteractionプラグインが読み込まれているか確認してください。
const calendar = new Calendar(calendarEl, {
// plugins などは前の例と同じ
selectable: true, // 範囲選択を有効にする
editable: true, // ドラッグ移動と長さ変更を有効にする
select(info) {
const title = prompt('予定名')
if (title) {
calendar.addEvent({ title, start: info.startStr, end: info.endStr, allDay: info.allDay })
}
calendar.unselect()
},
eventClick(info) {
if (confirm(`「${info.event.title}」を削除しますか?`)) info.event.remove()
},
async eventDrop(info) {
const res = await fetch(`/api/events/${info.event.id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ start: info.event.startStr, end: info.event.endStr }),
}).catch(() => null)
if (!res || !res.ok) info.revert() // 保存に失敗したら元の位置へ戻す
},
})
addEvent や remove は画面上の予定を変えるだけで、サーバーには何も送りません。eventDrop の例のように、保存APIの呼び出しと失敗時の info.revert() を組にしておくと、画面とデータベースの食い違いを防げます。eventResize にも同じ revert() があります。
データベース連携:JSONフィードのstart・endと応答形式
events にURLを渡すと、FullCalendarは表示中の期間を start と end のGETパラメータに入れて取りに来ます。前後の月へ移動したときやビューを切り替えたときに、必要な範囲だけ再取得する仕組みです。timeZone: 'Asia/Tokyo' で2026年10月の月表示を開いたとき、サーバーに届いたクエリは次のとおりでした(URLデコード済み)。
/api/events?start=2026-09-27T00:00:00+09:00&end=2026-11-08T00:00:00+09:00&timeZone=Asia/Tokyo
月表示は6週分を描くため、範囲は9月27日から11月8日までになります。end はその時刻を含まない排他的な終端です。サーバー側では、この範囲と少しでも重なる予定を返します。依存パッケージなしのNode.jsで書くと次のようになり、このサーバーとFullCalendarを組み合わせて、範囲外の12月の予定が返らないことを確認しました。
// server.mjs(Node.js 22以降・依存パッケージなし)
import http from 'node:http'
const events = [
{ id: '1', title: '定例会議', start: '2026-10-05T10:00:00+09:00', end: '2026-10-05T11:00:00+09:00' },
{ id: '2', title: '出張', start: '2026-10-14', end: '2026-10-17', allDay: true },
]
// 日付だけの値(終日予定)は日本時間の0時として扱う
const toMs = (v) => Date.parse(v.length === 10 ? `${v}T00:00:00+09:00` : v)
http.createServer((req, res) => {
const url = new URL(req.url, 'http://localhost')
if (url.pathname !== '/api/events') { res.writeHead(404).end(); return }
const from = Date.parse(url.searchParams.get('start'))
const to = Date.parse(url.searchParams.get('end')) // 排他的な終端
const hits = events.filter((e) => {
const start = toMs(e.start)
// end省略時はFullCalendarの既定の長さ(終日1日・時刻付き1時間)を補う
const allDay = e.allDay ?? e.start.length === 10
const end = e.end ? toMs(e.end) : start + (allDay ? 86400000 : 3600000)
return start < to && end > from
})
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify(hits))
}).listen(3000)
SQLで書くなら条件は WHERE start_at < :end AND end_at > :start です。start_at >= :start だけで絞ると、前月から続く連泊の出張のように、範囲の開始より前に始まる予定が消えます。パラメータ名は startParam・endParam で変えられるので、既存APIの引数名に合わせることもできます。
応答のJSONは、1件ごとに id・title・start を最低限持たせます。end は終日予定なら翌日の日付(10月14日から16日までの3日間なら "end": "2026-10-17")を入れます。APIを別ドメインに置く場合はCORSの設定も必要で、手順はCORSの仕組みとサーバー設定例の記事にまとめています。
React・Vue・Angularで使う場合のパッケージ
公式のコネクタは、v7でそれぞれのフレームワーク用パッケージの中にプラグインとテーマを抱える形になりました。Vanilla JS用の fullcalendar を別に入れる必要があるのはAngularだけです。
| フレームワーク | 標準パッケージ | 対応バージョン | Premium用 |
|---|---|---|---|
| React | @fullcalendar/react |
React 17〜19 | @fullcalendar/react-scheduler |
| Vue | @fullcalendar/vue3 |
Vue 3(Vue 2は終了) | @fullcalendar/vue3-scheduler |
| Angular | @fullcalendar/angular + fullcalendar |
Angular 16〜22 | @fullcalendar/angular-scheduler |
Reactのコネクタは、v6までは内部でPreactを動かすラッパーでしたが、v7ではReactで実装し直され、SSRとStrictModeでも動くようになりました(Preact用には @fullcalendar/preact が新設)。Preactとの違いはPreactの仕組みとReactとの違いの記事で詳しく扱っています。Reactでの最小構成は次のとおりです。
import FullCalendar from '@fullcalendar/react'
import dayGridPlugin from '@fullcalendar/react/daygrid'
import classicThemePlugin from '@fullcalendar/react/themes/classic'
import jaLocale from '@fullcalendar/react/locales/ja'
import '@fullcalendar/react/skeleton.css'
import '@fullcalendar/react/themes/classic/theme.css'
import '@fullcalendar/react/themes/classic/palette.css'
export default function Schedule() {
return (
<FullCalendar
plugins={[classicThemePlugin, dayGridPlugin]}
initialView="dayGridMonth"
locale={jaLocale}
events="/api/events"
/>
)
}
Angularは angular.json の styles に3枚のCSSを登録し、テンプレートで <full-calendar [options]="calendarOptions"> を使います。Angular 22への対応はv7.0.0で入りました。Angular側のバージョン事情はAngular 22.1の変更点の記事が参考になります。
FullCalendarを選ばないほうがよい場面
予定を並べて操作するUIならFullCalendarは第一候補ですが、次の条件に当てはまるなら別の手段を検討してください。
- 日付を1つ選ばせるだけの入力欄:カレンダー表示は過剰です。
<input type="date">か日付ピッカーのライブラリで足ります - 非公開の業務システムでタイムラインやリソース別の表示が必須:Premiumの商用ライセンスが開発者の人数分かかります。予算化できないなら、標準機能の週表示でリソースごとにカレンダーを並べる設計に変えるか、他のライブラリと比べてください
- Vue 2のまま改修を続けるシステム:v7のVue 2対応はありません。v6.1.21に固定し、Vue 3への移行と同時にv7へ上げる計画にします
- 同期ESMを読み込めないCommonJSのテスト環境:v7導入前にランタイムとテストランナーの対応確認が必要です。Node.js 20.19.0以降はrequire(esm)に対応しているため、Node.js 20という理由だけで移行必須とは判断できません
既存のv6を動かしているシステムを、機能追加の予定もないのにv7へ上げる必要はありません。CSS・色の設定名・パッケージ名の書き換えが一度に発生するため、画面改修のタイミングに合わせるのが現実的です。ライブラリのライセンスの考え方全般はOSSのライセンス種別と採用判断の記事で整理しています。
よくある質問
FullCalendarは無料で商用利用できますか?
Premiumと印の付いていない標準機能はMITライセンスで、商用のシステムでも無料で使えます。タイムライン表示やリソース表示などのPremium機能を非公開の商用システムで使う場合は、480ドルからの商用ライセンスが必要です。
FullCalendarの最新バージョンはいくつですか?
2026年9月27日時点の最新は7.1.0(2026年9月5日公開)です。npmで fullcalendar を入れるとv7が入ります。同日時点のv6系の最新リリースは6.1.21(2026年6月18日公開)です。
FullCalendarを日本語化するにはどうすればよいですか?
npmなら import jaLocale from 'fullcalendar/locales/ja' を locale に渡し、CDNなら locales/ja/global.js を読み込んで locale: 'ja' を指定します。月曜始まりにしたい場合は firstDay: 1 を追加します。
dateClickやドラッグ&ドロップが動かないのはなぜですか?
fullcalendar/interaction を plugins に入れていない可能性が高いです。ドラッグには editable: true、範囲選択には selectable: true も必要です。CDNの all/global.js にはinteractionが含まれています。
v6の解説記事のコードをv7で使えますか?
そのままでは動きません。@fullcalendar/core を fullcalendar に、@fullcalendar/daygrid などを fullcalendar/daygrid のサブパスに置き換え、skeleton.css とテーマのCSSを読み込む必要があります。@fullcalendar/daygrid を入れるとv6.1.21が入り、v7本体と混ざってエラーになります。