Package Exports
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (create-gas-app) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
create-gas-app
The modern CLI to scaffold Google Apps Script add-ons and standalone scripts — with your framework of choice.
npx create-gas-app@latest
# or
npx create-gas-app@latest my-gas-appFeatures
- Multi-framework — React (SWC), Vue 3, Svelte 5, SolidJS, all with TypeScript
- All GAS project types — Sheets Add-on, Docs Add-on, Forms Add-on, Standalone Script
- Type-safe server bridge — gas-client wired up with full TypeScript inference from your server functions
- Dev server with live reload — Vite + HTTPS iframe bridge for instant feedback inside GAS dialogs
- Addons — Tailwind CSS v4, shadcn/ui, Commitlint + Lefthook
add dialog— add new dialog entrypoints to an existing project- Zero runtime dependency — generated code is plain files, no
create-gas-applock-in
Usage
# Interactive (recommended)
npx create-gas-app@latest
# With a project name
npx create-gas-app@latest my-sheets-addon
# Add a dialog to an existing project
npx create-gas-app add dialog settingsInteractive prompts
create-gas-app — Google Apps Script, your way
What is your project named?
› my-gas-app
What type of Google Apps Script project?
● Sheets Add-on
○ Docs Add-on
○ Forms Add-on
○ Standalone Script
Which frontend framework?
● React (TypeScript + SWC)
○ Vue 3
○ Svelte 5
○ SolidJS
Select addons:
◼ Tailwind CSS v4
◼ shadcn/ui
◻ Commitlint + Lefthook
Which package manager?
● bun ○ pnpm ○ npm ○ yarn
Install dependencies now? Yes
Initialize a git repository? YesHow it works
Google Sheets / Docs / Forms
└── GAS runtime
├── code.js ← Vite bundles packages/server → IIFE
└── sidebar.html ← Vite bundles each dialog → single inlined HTML
└── React / Vue / Svelte / SolidJS app
└── gas-client ←→ google.script.run ←→ server functionsDev mode bridge
GAS only allows iframes from HTTPS origins. During development, clasp push deploys a lightweight wrapper HTML that iframes your local Vite dev server. A postMessage bridge proxies google.script.run calls to the real GAS backend so you get live reload without a full redeploy.
[GAS dialog]
└── dev-dialog-bridge.html
└── <iframe src="https://localhost:3000/sidebar/index.html">
└── Your Vite app
└── serverFunctions.getGreeting()
│ postMessage
dev-wrapper bridges ──→ google.script.run.getGreeting()Client dialogs are built as ESM (rollup output.format: "es") and loaded via an importmap in each dialog index.html to externalize framework libraries and gas-client.
Generated project structure
my-gas-app/
├── apps/
│ └── my-gas-app/
│ ├── package.json
│ ├── env.ts
│ └── dialogs/
│ ├── sidebar/
│ │ ├── index.html
│ │ └── src/
│ │ ├── main.tsx
│ │ └── App.tsx
│ └── about/
│ ├── index.html
│ └── src/
│ ├── main.tsx
│ └── App.tsx
├── packages/
│ ├── server/
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── env.ts
│ │ │ └── ui.ts
│ │ └── templates/
│ │ └── dev-dialog-bridge.html
│ ├── shared/
│ │ └── src/
│ │ ├── index.ts
│ │ ├── utils.ts
│ │ ├── utils/server.ts
│ │ └── styles/global.css
│ └── ui/
│ └── src/
│ └── index.ts
├── dist/ ← Built output (pushed to GAS via clasp)
├── vite.config.ts
├── appsscript.json ← GAS manifest
├── .clasp.json ← Created by `npm run clasp:create`
├── package.json
└── tsconfig.jsonGetting started with a generated project
1. Connect to Google Apps Script
# Login to your Google account
npx clasp login
# Create and link a new GAS project
npm run clasp:create2. Build and deploy
npm run deploy3. Open Apps Script
# Add-on projects (Sheets/Docs/Forms)
npm run clasp:open-container
# Standalone projects
npm run clasp:open:script4. (Optional) Local dev server
# Requires mkcert pre-installed: https://github.com/FiloSottile/mkcert
npm run setup:certs
npm run dev
# Open your Google Sheet → Extensions → My GAS App → Open
# Edits in apps/my-gas-app/dialogs/* hot-reload instantlyType-safe server calls
Server functions in packages/server/src/index.ts are automatically typed on the client:
// packages/server/src/index.ts
export const getUserData = async (
userId: string,
): Promise<{ name: string; plan: string }> => {
// runs in GAS
const data = PropertiesService.getUserProperties().getProperty(userId);
return JSON.parse(data ?? "{}");
};// apps/my-gas-app/dialogs/sidebar/src/App.tsx
import { serverFunctions } from "@my-gas-app/shared/utils/server";
// Fully typed — TypeScript knows the return type is { name: string; plan: string }
const data = await serverFunctions.getUserData("user-123");
console.log(data.name); // ✓
console.log(data.typo); // ✗ TypeScript errorAdding dialogs
# Add a new modal dialog
npx create-gas-app add dialog settingsThen register it in vite.config.ts:
const entrypoints = [
{
name: "Sidebar",
filename: "sidebar",
appDir: "sidebar",
template: "index.html",
},
{
name: "Settings",
filename: "settings",
appDir: "settings",
template: "index.html",
}, // added
];And add an opener in packages/server/src/ui.ts:
export const openSettingsDialog = () => {
const html = HtmlService.createHtmlOutputFromFile("settings")
.setWidth(800)
.setHeight(500);
SpreadsheetApp.getUi().showModalDialog(html, "Settings");
};Export it from packages/server/src/index.ts:
export { onOpen, onInstall, openSidebar, openSettingsDialog } from "./ui";Frameworks
| Framework | Plugin | Entry file |
|---|---|---|
| React | @vitejs/plugin-react-swc |
index.tsx |
| Vue 3 | @vitejs/plugin-vue |
index.ts + App.vue |
| Svelte 5 | @sveltejs/vite-plugin-svelte |
index.ts + App.svelte |
| SolidJS | vite-plugin-solid |
index.tsx |
Addons
Tailwind CSS v4
Uses the new CSS-first Tailwind v4 with @tailwindcss/vite. No tailwind.config.js needed.
shadcn/ui
Generates components.json and a starter Button component. Add more components with:
npx shadcn add card
npx shadcn add dialogOnly available with React (shadcn/ui's official support).
Commitlint + Lefthook
Enforces Conventional Commits via @commitlint/config-conventional and runs prettier on staged files via lefthook.
Scripts reference
| Script | Description |
|---|---|
dev |
Run deploy:dev, then start Vite dev server (HTTPS localhost:$PORT, default 3000) |
build |
Production build → dist/ |
build:dev |
Dev build (iframe wrappers) → dist/ using PORT (default 3000) |
deploy |
build + clasp:push |
deploy:dev |
build:dev + clasp:push |
clasp:login |
Authenticate with Google |
clasp:create |
Create a new GAS project |
clasp:push |
Push dist/ to GAS |
clasp:open:script |
Open Apps Script project in browser |
clasp:open:container |
Open linked container file (Sheets/Docs/Forms) |
setup:certs |
Generate local HTTPS certs with mkcert |
format |
Format all files with Prettier |
You can override the dev port per command:
PORT=5173 bun run devContributing
Contributions are welcome! See CONTRIBUTING.md for guidelines.
git clone https://github.com/vazhioli/create-gas-app
cd create-gas-app
bun install
bun run dev # watch mode
bun test-scaffold.ts # run integration testsLicense
MIT