Using Svelte with Google Apps Script

Published: Sunday, Nov 2, 2025
Cover

Earlier this year I wrote about my love of Google Apps Script to automate Calendar, Docs and Sheets. I recently looked into taking Google Apps Script even farther, leveraging its ability to serve HTML. Specifically, though, I wanted to see if I could build single-page apps (SPAs) using Svelte, my preferred UI framework. Turns out, you can and it’s really easy!

My quest to integrate Svelte with Google Apps Script started because I wanted to put a fancy roadmap UI on top of a Google Sheet of project data. I started doing it the hard way, i.e., hand-crafting HTML and Javascript by hand. While this worked, it was incredibly tedious - so many createElement, appendChild and addEventListener calls!

I know that Svelte simply creates HTML, Javascript, and CSS when you run the Svelte compiler (without any additional dependencies like React). So I figured it should be possible to build a UI in Svelte, compile it, and upload the files to Google Apps Script. This totally works, but there are a few tricks. Read on to learn more, or just jump to my skeleton repo: svelte-google-apps-script

Project Structure

In addition to your Svelte code in /src, you will need 2 additional files in the /gas folder:

  • Code.js
  • Index.html
Code.js

In gas/Code.js, you will put any server code you want to call from your application. But you also need 2 extra methods.

function doGet(e) {
  return HtmlService.createTemplateFromFile('Index')
    .evaluate()
    .setTitle('Svelte Google Apps Script')
    .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}

function include(filename) {
  return HtmlService.createHtmlOutputFromFile(filename).getContent();
}

The first function, doGet is what actually serves the HTML file. This is the entry point to your app. Update the setTitle call to set the title for your app.

The second function, include will be called by the Index.html to inject content from other HTML files that get generated when building your Svelte code.

Index.html

Inside gas/Index.html, there are 3 key lines. The first one is:

<?!= include("Stylesheet"); ?>

This injects the CSS code that will be generated by Svelte. This goes inside the head.

Inside the body tag, you need

<div id="app"></div>

This is the entry point for the Svelte app that Svelte will use to create the app UI.

In the body, you also need:

 <?!= include("Javascript"); ?>

This injects the Javascript code that will be generated by Svelte.

Compiling

Inside your package.json, there are three key lines

"gas:css": "echo '<style>' > 'gas/Stylesheet.html'; cat ./dist/assets/index*.css >> 'gas/Stylesheet.html'; echo '</style>' >> 'gas/Stylesheet.html'",
"gas:js": "echo '<script type="module">' > ./gas/Javascript.html; cat ./dist/assets/index*.js >> ./gas/Javascript.html; echo '</script>' >> ./gas/Javascript.html",
"postbuild": "npm run gas:css && npm run gas:js",

The first creates an HTML wrapper around the Svelte-generated CSS and puts it in gas/Stylesheet.html.

The second creates an HTML wrapper around the Svelte-generated Javascript and puts it in gas/Javascript.html.

The third runs both of these commands after the Svelte build is complete.

Clasp’ing

Once you have built your Svelte app, you can copy the contents of each file in the /gas folder to your Google Apps Script project. Or you can use Clasp to push the code to Google Apps Script.

You need a .clasp.json file like:

{
  "scriptId": "YOUR_APPS_SCRIPT_PROJECT_ID_FROM_SETTINGS",
  "rootDir": "gas"
}

Then from the command line you can log in via Clasp and push your code using npm run clasp:push.

Calling Server Functions

From your Svelte code, you can call server functions defined in Code.js using the google.script.run API. For example:

google.script.run.withSuccessHandler(handleResult).myServerFunction();

Where myServerFunction is defined in Code.js and handleResult is a function in your Svelte code that will process the result.

However, Typescript will complain about google being undefined. To fix this, you can add a // @ts-ignore above the call to google.script.run.

Alternatively, you can create a global.d.ts file in your /src folder with the following content:

declare global {
  interface Window {
    google: any;
  }
}

Developing Locally

Even though Clasp helps with deploying code quickly, it’s still slower than local development. But of course when developing locally, you can’t call Google Apps Script server functions.

To help with this, I add an extra snippet to my gas/Index.html file

 <script>
  // Indicate that we are running in a Google Apps Script environment
  // Helps to prevent errors when developing locally
  globalThis.inGAS = true;
</script>

Then in my Svelte code, I can check for this variable before calling any server functions. For example:

if (globalThis.inGAS) {
    const result = await google.script.run.withSuccessHandler(handleResult).myServerFunction();
} else {
    // Mock data or alternative logic for local development
    handleResult(mockData);
}

There are probably fancier ways to do this, but this works well for me. You can see this in use in the roadmap branch of the skeleton repo.