スプレッドシート更新だけでOK!Studio × Firebase で作る「自動更新カレンダー完全ガイド」
毎月のカレンダー更新をもっと楽にしたい人へ。Studio のデザインそのままで、スプレッドシートから自動反映される診療・営業カレンダーを構築する方法を詳細に解説。Googleカレンダー不要で、ブランドに合わせた自由なデザインが可能です。
医療サイト、美容サロン、習い事教室、イベントサイトなどで欠かせない「毎月のカレンダー更新」。従来は「Googleカレンダーの埋め込み」が一般的です。
ただGoogleカレンダーの埋め込みでは、せっかくのStudioなのにデザインの自由度がなく、休診日・特別営業日・午後休診などの細かい情報をサイトのデザインに合わせて反映させるのは困難です。
この記事では、スプレッドシート、Firebase、Studio を組み合わせて、非エンジニアでも簡単に更新できる診療カレンダーを構築する方法を紹介します。デザインは Studio のまま自由に作り、データはスプレッドシートから自動反映される運用しやすい仕組みです。
※この連携カレンダー実装には、Studioの有料プランの契約が必要です。
この仕組みでできること
- スプレッドシート編集だけでカレンダーが更新される
- Studio側は見た目そのままで日付やラベルのみ自動反映
- 休診日、午後休診、特別営業日の表示が可能
- 月送り(前月、翌月)ボタンに対応
- スプレッドシートの編集者はコード不要
- Firebase でデータを安全に管理できる
▼完成プレビュー(スプレッドシート側の編集画面)

全体構成
▼構成イメージ

スプレッドシート ↓(データ編集) Google Apps Script(GAS) ↓(データ自動転送) Firebase Firestore ↓(データ登録) Studio(カスタムコードで取得しカレンダー表示)
1. スプレッドシートの準備
新しいスプレッドシートを作成
Studioのカレンダーと連携をさせるカレンダーをスプレッドシートで作成します。

シート名の変更
シート名を「カレンダー」に変更します。⚠️ GASでシート名に相違があるとエラーが発生するので注意

A列〜F列の項目名を設定
A1〜F1のセルに下記を入力してください。⚠️ GASでセル入力テキストに相違があるとエラーが発生するので注意
- A1:
年月日 - B1:
year - C1:
month - D1:
day - E1:
weekday - F1:
label

セルA2〜F2までの入力
セルA2には「2025/11/1」の形式で日付を入力します。A3以降も「2025/11/2」、「2025/11/3」・・・、と連続して日付順に入力します。

A列に日付を入力すると、B列〜E列が自動で計算されるように、B列〜E列に以下の関数を入力してください。B3〜E3以降も同様に関数をコピーして入力する。
B2:=IF($A2="","",YEAR($A2))
C2:=IF($A2="","",MONTH($A2))
D2:=IF($A2="","",DAY($A2))
E2:=IF($A2="","",CHOOSE(WEEKDAY($A2),"日","月","火","水","木","金","土"))
F列にはカレンダーに表示をしたいラベル名を入力規則で指定してください。▼ラベル名の例:休診日、午後休診

連携スプレッドシートの完成
これでStudioと連携するカレンダー(スプレッドシート)が完成しました。

2. Firebase の新規登録
ここでは、Firebase を使ったことがない人でも設定できるように、最初の登録から Firestore 作成までの手順を解説します。既に登録済みの方はこのステップを飛ばしてください。
Firebase アカウント作成
https://firebase.google.com/にアクセス- 「コンソールへ移動」
- Googleアカウントでログイン

新規プロジェクト作成
-
「プロジェクトを追加」

Firebaseコンソールの「新しいFirebaseプロジェクトを作成」ボタン -
任意のプロジェクト名を入力(例:
my-clinic-calendar)
プロジェクト名入力欄にmy-clinic-calendarと入力した状態 -
Google アナリティクスは「無効」でOK

Googleアナリティクスを無効にする設定画面 -
プロジェクト作成を完了

「Firebaseプロジェクトが準備できました」完了画面
Firestore の作成
-
左メニューから「構築」→「Firestore Database」を選択

Firebase左メニューでFirestore Databaseを選択した状態 -
「データベースを作成」

Cloud Firestoreの「データベースの作成」ボタン -
「Standardエディション」を選択して次へ

エディション選択でStandardエディションを選んだ状態 -
「データベースID」はデフォルト、「ロケーション」はリージョン「asia-northeast1(東京)」を選択して次へ

データベースIDをデフォルト、ロケーションをasia-northeast1(Tokyo)に設定 -
「構成」は「本番環境モードで開始する」を選択し作成完了

「本番環境モードで開始する」を選択した構成画面
Web アプリの追加
-
下部の「アプリを追加」を押して、Web を選択(
<>アイコン)
「アプリにFirebaseを追加する」ダイアログでWebアイコンを選択 -
アプリのニックネームを入力(自由)して「アプリを登録」する。

アプリのニックネームに「Studio連携カレンダー」と入力 -
表示された
firebaseConfigを控えておきます。
firebaseConfigのコード表示とコピーボタン
APIキーの注意点
FirebaseのWeb APIキーは、秘密情報ではありません。ただし、悪用されないために以下だけ注意します。
- カレンダー情報だけは GAS から書き込めるようにし、それ以外のコレクションは全て拒否します。
- 公開範囲を
calendar_daysコレクションのみに絞ることで、リスクを最小限にします。
Firestore ルール設定
Firestore Database の「ルール」に以下を書き換えをします。これで書き込みはGASのみ、Studioは読み取りだけになります。
▼Firestore Databaseルール(コピー用)
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// ▼ カレンダー用のコレクションだけ公開
match /calendar_days/{docId} {
// 読み取り:誰でもOK(サイト上で公開情報として見せる前提)
allow read: if true;
// 書き込み:GAS からの REST 呼び出しも含めて許可
// ※ カレンダー情報だけなので、ここは割り切りで true でもOK
allow write: if true;
}
// それ以外のコレクションは全拒否
match /{document=**} {
allow read, write: if false;
}
}
}

コレクションの登録
-
Firestore Databaseにコレクションを登録します。

Firestore Databaseの「コレクションを開始」ボタン -
「コレクションID」に
calendar_daysを入力して次へ。
コレクションIDにcalendar_daysと入力 -
ドキュメントIDとフィールドの設定を下記の通りに設定して「保存」。

ドキュメントID 2025-11-1、フィールドyear/month/day/labelの設定
ドキュメントIDの登録は1つで問題ありません。スプレッドシートと連携が完了すると、自動的に登録がされます。以上で、Firebaseの設定は完了です。
3.Google Apps Script(GAS)で Firestore に自動同期する
GAS を使って、スプレッドシートのデータを Firestore に自動で書き込みがされるように設定をしていきます。
Google Apps Scriptを開く
Studio連携カレンダーのスプレッドシートを開いて、「拡張機能」→「Apps Script」へ

スクリプトプロパティの追加
画面左側のメニューから「プロジェクトの設定」を選択して、「スクリプトプロパティを追加」をします。

下記の通りに2つのスクリプトプロパティを設定します。
FIREBASE_PROJECT_ID:ご自身のIDFIREBASE_API_KEY:ご自身の APIキー

GASコードの編集
下記のコードをコピーして貼り付けて「プロジェクトを保存」してください。
▼GAS コード(コピー用)
const SCRIPT_PROP = PropertiesService.getScriptProperties();
const FIREBASE_PROJECT_ID = SCRIPT_PROP.getProperty('FIREBASE_PROJECT_ID');
const FIREBASE_API_KEY = SCRIPT_PROP.getProperty('FIREBASE_API_KEY');
const CALENDAR_COLLECTION =
SCRIPT_PROP.getProperty('CALENDAR_COLLECTION') || 'calendar_days';
const BASE_URL =
`https://firestore.googleapis.com/v1/projects/${FIREBASE_PROJECT_ID}/databases/(default)/documents/${CALENDAR_COLLECTION}`;
function updateFirestoreFromSheet() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('カレンダー');
const lastRow = sheet.getLastRow();
if (lastRow < 2) return;
const values = sheet.getRange(2, 2, lastRow - 1, 5).getValues();
let successCount = 0;
values.forEach((row, index) => {
const [year, month, day, weekday, label] = row;
if (!year || !month || !day) return;
const payload = {
fields: {
year: { integerValue: Number(year) },
month: { integerValue: Number(month) },
day: { integerValue: Number(day) },
label: { stringValue: String(label || "") }
}
};
const docId = `${year}-${month}-${day}`;
const url = `${BASE_URL}/${docId}?key=${FIREBASE_API_KEY}`;
UrlFetchApp.fetch(url, {
method: 'PATCH',
contentType: 'application/json',
payload: JSON.stringify(payload),
muteHttpExceptions: true
});
successCount++;
});
Logger.log(`Firestore 更新完了: 成功 ${successCount}件`);
}

トリガーの設定
-
画面左側のメニューから「トリガー」をクリックして、「トリガーを追加」をします。

Apps Scriptの「トリガーを追加」ボタン -
トリガーの設定を下記の通りに設定して「保存」。

トリガー編集画面(関数・イベントソース・イベントの種類の設定)
⚠️上記のような警告画面が出たら、
-
警告画面が出たら、左下の「Advanced(詳細)」をクリック

「Googleがこのアプリを確認していません」警告画面のAdvancedリンク -
「Go to 'プロジェクト名' (unsafe)」というリンクが出るのでクリック
-
権限確認の画面に進むので、スプレッドシートの編集・外部サービスへの接続(UrlFetch)などを許可
承認が終わると、トリガーの保存が通り、「updateFirestoreFromSheet」のトリガーが有効化されます。以上で、Google Apps Script(GAS)の設定は完了です。
👇 試しにコードを実行して正しく「実行完了」するかを確認してください。

4.Studio のカレンダーデザイン構成と ID 設定
ここからは、Studio 側でカレンダーの見た目を作り、JavaScript から日付やラベルを流し込めるようにする手順を解説します。ポイントは次の2つです。
- 見た目はすべて Studio のデザインエディタで作る
- JavaScript は「どのマスが何番目か」を ID で認識する
このために、カレンダー全体や各マスに ID を付けていきます。
▼完成イメージ

カレンダー全体の親ボックスを用意する
カレンダーを表示したいセクション内に、カレンダー用の親ボックスを1つ追加します。そのボックスを選択し、設定から id をcalendar_rootに設定します。このcalendar_rootの中に、「タイトル」「曜日行」「日付マス」を配置していきます。

タイトル部分に「2025年11月」などのテキストを入れたい場合は、カレンダーの親ボックスの中にテキストパーツを1つ入れておきます。そのテキストパーツには id としてcal_titleを設定します。JavaScript からこの要素に対して「yyyy年 m月」という文字列を書き込みます。

7列×6行ぶんの「日付マス」を作成する
1カ月分のカレンダーは、最大6週ぶん必要になるため、Studio 上では「7列 × 6行 = 42マス」の日付ボックスをあらかじめ用意します。手順は次の通りです。
-
まず、1日分の「日付マス」のデザインを作ります。例として、次のような構成にします。外側のボックス(マス全体)、中に「日付の数字」用テキスト、さらに「ラベル」用テキスト(休診日など)

1日分の日付マスのデザイン例 -
この1マスをベースにして、合計で42個ぶん複製します。7列 × 6行のグリッドになるように配置します。レイアウトは、Flex や Grid、テーブル風レイアウトなど、どの方法でも構いません。

42マス分のグリッドに複製した状態 -
複製ができたら、それぞれの要素に下記の通り ID を付けます。この作業がカレンダー実装において一番苦労する部分です。休憩を挟みながらのんびり作業を進めてください。☕️
1〜42個全てに
cal_cell_XX/cal_day_XX/cal_label_XXのIDを付けます。- マス全体のボックス →
cal_cell_1〜cal_cell_42 - マス内の日付テキスト →
cal_day_1〜cal_day_42 - マス内のラベルテキスト →
cal_label_1〜cal_label_42
左上のマスから順番に番号を振っていくイメージです。

各マスにcal_cell/cal_day/cal_labelのIDを設定するイメージ - マス全体のボックス →
ここで重要なのは、見た目のデザインや余白、背景色などはすべて Studio 側で自由に設定してよいという点です。JavaScript は、あくまで各マスの中のテキストを書き換えるだけです。
曜日ラベルを表示したい場合
カレンダーの一番上に「日、月、火、水、木、金、土」のような曜日行を表示したい場合は、親ボックスcalendar_rootの中に、曜日用の行を追加します。この曜日行は、JavaScript で動的に書き換える必要がないため、ID の設定は不要です。Studio 上で単純に固定テキストとして「日 月 火 水 木 金 土」を配置しておけば問題ありません。

月送りボタンを配置する
月送り(前月、翌月)機能を使うために、Studio側でボタンを2つ作成します。
- 前月ボタンとして使いたいテキストやボックスを作成し、id を
cal_prevに設定します。例:「< 前の月」というテキストボタン。 - 翌月ボタンとして使いたいテキストやボックスを作成し、id を
cal_nextに設定します。例:「次の月 >」というテキストボタン。
この2つのボタンは、カレンダーのタイトルの左右に配置することが多いです。レイアウトは自由ですが、必ず id だけはcal_prevとcal_nextにしておきます。


5.Studio のカスタムコード設置
ここでは、作成した Firebase 読み取り用の JavaScript を Studio に正しく設置する方法を説明します。Studio では、コードを入力する場所が複数あるため、どこに貼るべきかを丁寧に解説します。この手順どおりに進めれば、初心者の方でもカスタムコードを正しく動作させることができます。
カスタムコードの配置場所について
Studio にはコードを設置できる場所が2つあります。
- サイト設定のカスタムコード
- ページ設定のカスタムコード
今回のカレンダーコードは「ページごとに実装する」ため、カレンダーを表示したいページのbody内の末尾に貼り付けます。
Studio の仕様として、head 末尾 → HTML読み込み前に実行、body 末尾 → HTML読み込み完了後に実行(今回はこちら)の違いがあります。カレンダーは Studio のボックス(DOM 要素)を書き換える必要があるため、必ずbody内の末尾を使用します。
6.カスタムコードの設置手順
手順1:Studio のページ設定を開く
- カレンダーを設置したページを選択
- 画面右上の「ページ」をクリック
- 右メニュー下にある「カスタムコード」を選択
これでコードを入力できる画面が開きます。
手順2:Firebase連携のカスタムコードを貼り付ける
カスタムコード編集画面には、次の2つの入力欄があります。
- head 内の末尾
- body 内の末尾
必ずbody内の末尾にコードを貼ってください。
理由・・・Studio の仕様として、body 内の末尾に書いたコードは画面要素が読み込まれた「後に」確実に実行されるため、日付マスを書き換える今回の処理と相性が良いためです。

▼カスタムコード(コピー用)
<script type="module" id="calendar-firebase-script">
import { initializeApp } from "https://www.gstatic.com/firebasejs/10.13.0/firebase-app.js";
import {
getFirestore,
collection,
query,
where,
getDocs
} from "https://www.gstatic.com/firebasejs/10.13.0/firebase-firestore.js";
// ========= Firebase Config =========
const firebaseConfig = {
apiKey: "あなたのAPI_KEY",
authDomain: "あなたのAUTH_DOMAIN",
projectId: "あなたの_PROJECT_ID"
};
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
// 今日の年月から開始
const today = new Date();
let currentYear = today.getFullYear();
let currentMonth = today.getMonth() + 1;
loadMonth(currentYear, currentMonth);
const prevBtn = document.getElementById("cal_prev");
const nextBtn = document.getElementById("cal_next");
if (prevBtn) {
prevBtn.addEventListener("click", () => {
currentMonth--;
if (currentMonth < 1) {
currentMonth = 12;
currentYear--;
}
loadMonth(currentYear, currentMonth);
});
}
if (nextBtn) {
nextBtn.addEventListener("click", () => {
currentMonth++;
if (currentMonth > 12) {
currentMonth = 1;
currentYear++;
}
loadMonth(currentYear, currentMonth);
});
}
function loadMonth(year, month) {
const ref = collection(db, "calendar_days");
const q = query(ref, where("year", "==", year), where("month", "==", month));
const dayMap = new Map();
getDocs(q)
.then((snap) => {
snap.forEach((doc) => {
const d = doc.data();
dayMap.set(d.day, { label: d.label || "" });
});
renderCalendar(year, month, dayMap);
})
.catch((err) => {
console.error("カレンダーデータ取得エラー", err);
renderCalendar(year, month, new Map());
});
}
function renderCalendar(year, month, dayMap) {
const titleEl = document.getElementById("cal_title");
if (titleEl) {
titleEl.textContent = `${year}年 ${month}月`;
}
const firstDate = new Date(year, month - 1, 1);
const firstDayIdx = firstDate.getDay();
const daysInMonth = new Date(year, month, 0).getDate();
const totalCells = 42;
for (let i = 0; i < totalCells; i++) {
const cellIndex = i + 1;
const dayNum = i - firstDayIdx + 1;
const cellEl = document.getElementById(`cal_cell_${cellIndex}`);
const dayEl = document.getElementById(`cal_day_${cellIndex}`);
const labelEl = document.getElementById(`cal_label_${cellIndex}`);
if (!cellEl || !dayEl || !labelEl) continue;
// ===== 初期化 =====
cellEl.classList.remove("cal--closed", "cal--pmclosed", "cal--normal");
labelEl.classList.remove("label--closed", "label--pmclosed", "label--normal");
dayEl.textContent = "";
labelEl.textContent = "";
labelEl.style.display = "none";
// 当月外
if (dayNum < 1 || dayNum > daysInMonth) continue;
// 日付
dayEl.textContent = String(dayNum);
// データ取得
const info = dayMap.get(dayNum);
if (!info) {
// 通常日
cellEl.classList.add("cal--normal");
continue;
}
const label = info.label || "";
const normalized = label.replace(/\s/g, "");
if (label) {
labelEl.textContent = label;
labelEl.style.display = "block";
}
// ===== 背景色 / ラベル色 まとめて適用 =====
if (normalized === "休診日") {
cellEl.classList.add("cal--closed");
labelEl.classList.add("label--closed");
} else if (normalized === "午後休診") {
cellEl.classList.add("cal--pmclosed");
labelEl.classList.add("label--pmclosed");
} else {
cellEl.classList.add("cal--normal");
labelEl.classList.add("label--normal");
}
}
}
</script>
⚠️ 上記コードに含まれている下記のconst firebaseConfigの部分は、ご自身のAPI KEY等に書き換えてください。
// ========= Firebase Config =========
const firebaseConfig = {
apiKey: "あなたのAPI_KEY",
authDomain: "あなたのAUTH_DOMAIN",
projectId: "あなたの_PROJECT_ID"
};
手順3:マス・ラベルカラー用のCSSコードを貼り付ける(任意)
カレンダーのマスやステータス(休診等)によって背景色などの変更をしたい場合は、下記のコードを、head内の末尾に貼ってください。カラー変更が不要であれば、この手順はスキップしてください。
カスタムコード内のカラーコードを変更することで、ステータスに応じたカラー設定が可能です。
▼カスタムコード(コピー用)
<style>
/* ===== マス全体(cell) ===== */
.cal--normal {
background-color: #ffffff !important;
color: #333333 !important;
}
.cal--closed {
background-color: #d8d8d8 !important;
color: #ffffff !important;
}
.cal--pmclosed {
background-color: #ffe1a8 !important;
color: #333333 !important;
}
/* ===== ラベル部分(label) ===== */
.label--normal {
background-color: transparent !important;
color: #333333 !important;
}
.label--closed {
background-color: #ff6b6b !important;
color: #ffffff !important;
}
.label--pmclosed {
background-color: #ffd55e !important;
color: #333333 !important;
}
</style>
手順4:公開して動作確認する
Studioのデザインエディタやプレビューでは JavaScript が動かないため、必ず公開ページで動作確認を行ってください。確認するポイントは次の5つです。
- タイトルに「2025年11月」などが表示されている
- 日付が 1〜月末まで正しく配置されている
- ラベル(休診日など)が表示されている
- 前月・翌月ボタンが動く
- スプレッドシートを更新すると反映される
これらがすべて問題なければ「完成」です。
まとめ
最後までお読みいただきありがとうございます。「Studio」のデザイン性と、スプレッドシートの更新しやすさ、さらに Firebase の柔軟なデータ管理を組み合わせることで、非エンジニアでも毎月ラクに更新できる診療カレンダーを作れるようになりました。
ポイントを振り返ると、次の通りです。
- スプレッドシートを編集するだけで、カレンダーが自動更新される
- Studio 側は"見た目はそのまま"で日付とラベルだけが切り替わる
- 休診日・午後休診・特別営業などの細かな表示にも対応
- 前月・翌月の切り替えも可能
- Firebase を使うことで、安全にデータを読み込める仕組みが構築できる
- 更新作業にはコードが不要で、運用負荷が大幅に軽減される
Googleカレンダー埋め込みでは実現できなかったブランドに合わせたデザイン×運用しやすさの両立が、この構成でようやく実現できます。「毎月のカレンダー更新をもっと簡単にしたい」「クリニック・美容サロン・スクールなどで休業日を柔軟に管理したい」そんな方には、とても相性の良い仕組みです。ぜひ、このカレンダー構成を活用して、より運用しやすく、より美しいサイト運営を実現してみてください。
