Observable Framework 1.9.0 GitHub️ 2.0k


You can host your built Framework project on any static site hosting service, or self-host it with any static site server. This guide covers deploying to Observable, which is the easiest way to host your Framework project as support is built-in. We’ll also cover setting up automated deploys with GitHub Actions.

If you don’t already have a project ready to deploy, create one by following our Getting started guide.

Manual deploys

First, make sure that your project builds without error:

npm run build

Once that is done you can deploy to Observable:

npm run deploy

The first time you deploy a project, you will be prompted to configure the project’s slug (which determines its URL), access level, and other details. If you aren’t yet signed-in to Observable, you will also be prompted to sign-in.

When the deploy command finishes, it prints a link to observablehq.cloud where you can view your deployed project. If you choose private as the access level, that link will only be accessible to members of your Observable workspace. (You can invite people to your workspace by going to observablehq.com.) If you chose public, you can share your project link with anyone. You can change the access level of a project later from your workspace projects page.

To see more available options when deploying:
npm run deploy -- --help

Deploy configuration

The deploy command creates a file at .observablehq/deploy.json under the source root (typically src) with information on where to deploy the project. This file allows you to re-deploy a project without having to repeat where you want the project to live on Observable.

The contents of the deploy config file look like this:

  "projectId": "0123456789abcdef",
  "workspaceLogin": "acme",
  "projectSlug": "hello-framework"

A deploy config file is required for automated deploys. You will need to commit this file to git to deploy via GitHub Actions.

To store the deploy config file somewhere else, use the --deploy-config argument. For example, to create a “staging” deploy to share early versions of you project, you could use a deploy-staging.json like so:

npm run deploy -- --deploy-config=src/.observablehq/deploy-staging.json

If the specified config file does not yet exist, you will again be prompted to choose or create a new project; the resulting configuration will then be saved to the specified file. You can re-deploy to staging by passing the same --deploy-config argument; or you can deploy to “production” by not specifying the --deploy-config argument to use the default deploy config.

Automated deploys

To set up automatic deploys (also known as continuous deployment or CD), we recommend GitHub Actions. In your git repository, create and commit a file at .github/workflows/deploy.yml. Here is a starting example:

name: Deploy

  # Run this workflow whenever a new commit is pushed to main.
  push: {branches: [main]}
  # Run this workflow once per day, at 10:15 UTC
  schedule: [{cron: "15 10 * * *"}]
  # Run this workflow when triggered manually in GitHub’s UI.
  workflow_dispatch: {}

    runs-on: ubuntu-latest
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
          node-version: 20
          cache: npm
      - run: npm ci
      - run: npm run build
      - name: Deploy to Observable Cloud
        # This parameter to `--message` will use the latest commit message
        run: npm run deploy -- --message "$(git log -1 --pretty=%s)"
          # Authentication information. See below for how to set this up.
As shown above, deploy messages can be set using --message. This is especially useful for continuous deployment from a git repository: the message can include the SHA, author, and message of the latest commit.

When deploying automatically, you can’t sign-in in your browser the way you did for manual deploys; instead, your GitHub action will authenticate using an Observable API key (also known as a token and referred to as OBSERVABLE_TOKEN above).

To create an API key:

  1. Open the API Key settings for your Observable workspace.
  2. Click New API Key.
  3. Check the Deploy new versions of projects checkbox.
  4. Give your key a description, such as “Deploy via GitHub Actions”.
  5. Click Create API Key.

The token you create is the equivalent of a password giving write access to your hosted project. Do not commit it to git or share it with anyone you don’t trust. If you accidentally expose your key, you can go back to your settings to immediately revoke it (and create a new key).

In a moment, you’ll copy-paste your new Observable API key, so leave this window open for now. (If you accidentally close the window, you can delete the old key and create a new one. For security, API keys are only shown once upon creation.)

To authenticate with Observable and to access the Observable API key securely from our Github action, we’ll use a GitHub secret.

To create a GitHub secret, in a new window:

  1. Go to your GitHub repository.
  2. In the top navigation, click Settings.
  3. In the left sidebar, click Secrets and variables, then Actions.
  4. Click New repository secret.
  5. In the Name field, enter OBSERVABLE_TOKEN.
  6. In the Secret field, paste the API key you created on Observable.
  7. Click Add secret.

After you’ve performed these steps, the deploy.yml above will automatically build and deploy your project once per day (to keep your data up-to-date), as well as whenever you push a new version of the code to your repository (so you can make changes at any time).


If some of your data loaders take a long time to run, or simply don’t need to be updated every time you modify the code, you can set up caching to automatically re-use existing data from previous builds. To do that, add the following steps to your deploy.yml before you run build:

      # ...
      - id: date
        run: echo "date=$(TZ=America/Los_Angeles date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
      - id: cache-data
        uses: actions/cache@v4
          path: src/.observablehq/cache
          key: data-${{ hashFiles('src/data/*') }}-${{ steps.date.outputs.date }}
      # ...

This uses one cache per calendar day (in the America/Los_Angeles time zone). If you deploy multiple times in a day, the results of your data loaders will be reused on the second and subsequent runs. You can customize the date and cache-data steps to change the cadence of the caching. For example you could use date +'%Y-%U' to cache data for a week or date +'%Y-%m-%dT%H to cache it for only an hour.

You’ll need to edit the paths above if you’ve configured a source root other than src.

Other hosting services

Observable Framework builds a set of static files that can be hosted by any static site hosting services. To build your project, run:

npm run build

Then upload the contents of your dist directory to your static service of choice.

By default, Framework generates “clean” URLs by dropping the `.html` extension from page links. Not all webhosts support this; some need the cleanUrls config option set to false.
When deploying to GitHub Pages without using GitHub’s related actions (configure-pages, deploy-pages, and upload-pages-artifact), you may need to create a .nojekyll file in your dist folder after building. See GitHub’s documentation on static site generators for more.