All Collections

Filesystem

When you clone a branch, the CLI writes out a normalized 'shredded' Bubble app. This article is the reference for what lives where, what shape it takes, and what you must not touch.

The filesystem is not a faithful JSON dump of your Bubble app. It is a projection designed for editing: volatile fields are stripped, derived fields are recomputed on apply, and several fields are lifted into directory structure.

Top-level layout

my-app/                       # app root (one per Bubble app)
  .buildprint/                # internal state — never edit
    app.json                  # { schemaVersion, appId, tokenRef }
    remote.git/               # shared bare repo
  test/                       # branch workspace (git worktree)
    .git                      # gitlink into the shared bare repo
    .gitignore                # ignores /.buildprint/
    app.json                  # top-level Bubble scalars
    settings/
    data_types/
    option_sets/
    styles/
    pages/
    element-definitions/
    mobile-views/
    global-elements/
    api/

The app root is shared across every branch of one Bubble app. Each Bubble branch materializes as a sibling folder inside it. The branch folder name must match the checked-out git branch — see Workspace for the lifecycle and safety rails.

Branch folder contents

Each directory inside a branch folder maps to a Bubble concept:

  • app.json — top-level scalars for the app (name, plan, defaults). Fields that belong elsewhere have been lifted out.

  • settings/ — Bubble settings. settings/client-safe.json holds the main settings body; settings/api-connector/<plugin-id>/plugin.json and settings/api-connector/<plugin-id>/calls/<call-id>.json hold API connector definitions.

  • data_types/<type-id>/type.json — one folder per data type. Field list is inline in the body.

  • option_sets/<set-id>/option-set.json — one folder per option set. Values are inline, ordered by sort_factor.

  • styles/ — styles grouped by element type: styles/<ElementType>/<style-id>.json. Design tokens are lifted into sibling files: styles/tokens.json, styles/defaults.json, styles/fonts.json.

  • pages/<page-id>/ — one folder per page. Body file page.json; elements under elements/; workflows under workflows/ (see below).

  • element-definitions/<id>/ — reusable elements. Same shape as pages; body file is reusable.json.

  • mobile-views/<id>/ — mobile screens. Same shape as pages.

  • global-elements/<id>/ — global elements (app-wide surfaces). Same shape as pages.

  • api/ — backend workflows, optionally grouped into Bubble workflow folders (see Workflow folders below).

Pages, reusables, mobile views, global elements

These four directories all use the same structure. The body file at the root of the folder holds the entity's properties; children elements and workflows live in sibling subdirectories.

pages/<page-id>/
  page.json                   # page properties + children manifest
  elements/
    <element-key>/
      element.json            # element properties, type, style, states
      <child-element-key>/    # nested folder per child element
        element.json
        <grandchild-key>/
          element.json
  workflows/
    <workflow-id>/
      workflow.json           # trigger and metadata
      actions/
        0.json                # integer filenames = insertion order
        1.json
        2.json

Element folders

  • The folder name is the element's map key (the key it has inside its parent's elements object), not the id field inside the body. These can differ; references in other parts of the app use the map key.

  • Child elements live as subfolders of their parent. The tree on disk mirrors the containment tree in Bubble.

Workflows and actions

  • Each workflow gets its own folder containing workflow.json (trigger, metadata) and an actions/ folder with one file per action.

  • Action filenames are stringified integers0.json, 1.json, 2.json. They encode execution order.

  • Gaps in the sequence are valid and preserved verbatim (some Bubble structures intentionally use sparse integer keys).

Workflow folders

Bubble lets you group workflows into named folders. On disk, a workflow folder is a directory with a config.json alongside the workflows it contains:

api/
  <folder-id>/
    config.json               # { "name": "Billing" }
    <wf-id>/
      workflow.json
      actions/0.json
  <wf-id>/                    # workflow with no parent folder
    workflow.json
    actions/0.json
  • Folder metadata lives in config.json. To rename a folder, edit that file.

  • To move a workflow between folders, move the workflow directory itself.

  • Workflow folders under api/ cannot be nested — one level only. The api-folder-flat check rule enforces this.

  • Workflow bodies do not carry a wf_folder field — the parent folder is derived from the directory tree. Adding that field to workflow.json does nothing; rearrange the folders instead.

The same structure applies under pages/<page-id>/workflows/, element-definitions/<id>/workflows/, mobile-views/<id>/workflows/, and global-elements/<id>/workflows/.

Styles

Styles are split by element type. Each subdirectory contains one JSON file per style:

styles/
  Button/
    primary.json
    secondary.json
  Text/
    heading.json
    body.json
  tokens.json                 # color_tokens, color_tokens_user
  defaults.json               # default_styles, default_icon_set
  fonts.json                  # font_tokens, font_tokens_user, fonts

The three lifted files at the top of styles/ were moved out of settings/ so design tokens live in one predictable place.

Canonical JSON form

Every JSON file the CLI writes goes through a single canonical formatter. The rules:

  • UTF-8 with LF line endings and a trailing newline.

  • Two-space indentation. One value per line in objects; one element per line in non-trivial arrays.

  • No tabs, no CR characters, no trailing whitespace.

  • Non-integer-keyed object keys are alpha-sorted. Integer-keyed maps (workflow actions, element states, TextExpression.entries) preserve insertion order.

Byte-identical output for the same input keeps sync diffs clean and merge conflicts minimal. If you hand-edit a file and the formatting drifts, the canonical-form check rule will flag it — re-run the command that produced the file, or run it through a JSON formatter.

IDs and filenames

  • Buildprint mints its own IDs.

  • Fresh IDs come from buildprint utils generate-ids <n> (one per line, lexicographic order, between 1 and 100 at a time). Call this before creating entities by hand.

  • Existing apps may contain two Bubble ID formats (short-uid and timestamp-random). Readers handle both; writers always emit the canonical Buildprint form.

  • Keys with the _bp_ or bp- prefix are reserved for the CLI. You'll occasionally see internal marker files and sentinels (for example __bp_dir__.json or a __bp_no_type__/ folder under styles) — treat them as internal and don't edit them by hand.

Children manifests

Every parent that contains elements keeps a manifest of its children in its body file:

{
  "children": ["hdr_0001", "content_0002", "footer_0003"]
}

The invariant: the set of child subfolders under elements/ must match the set of keys listed in children, and the order in children is the canonical display order. Dangling entries or orphaned folders are errors flagged by the children-manifest check rule.

Render order is separate: properties.order (flex order) and properties.zindex (stacking) describe CSS concerns and live on individual elements when set. The CLI does not invent values for these — only Bubble does.

What not to edit

  • .buildprint/ — the shared bare git repo and app config. Gitignored in the worktree. Leave it alone.

  • .git/ internals — treat the worktree like any other git checkout.

  • Lifted fields - anything that was moved out of a body file into directory placement. Writing properties.wf_folder back into workflow.json, or type back into a style body, has no effect and may trigger check errors.

  • Volatile fields - if you see a stray last_change, creation_date, _id, screenshot, or uid_counter field creep back in from a hand edit, remove it. The CLI strips these on shred and will not regenerate them.

  • Internal marker files - sentinels under __bp_* folders or files. They exist so the CLI can round-trip edge cases (case-insensitive collisions, styles with no type). Don't author or rename them.

Git model

The branch folder is a real git worktree backed by the bare repo at <app-root>/.buildprint/remote.git/. You can use git status, git diff, git log, git add, and git commit as normal.

Four refs are worth knowing about:

  • refs/heads/<branch> — your local commits. This is what you edit on.

  • refs/remotes/buildprint/<branch> — the last local commit that was successfully applied to Bubble. Use git log buildprint/<branch>..HEAD to see what's locally ahead.

  • refs/bubble/<branch> — the latest Bubble snapshot (lives in the bare repo). buildprint sync updates this ref and merges it into your branch.

  • refs/published/<branch> — bookkeeping ref inside the bare repo, mirrored out to buildprint/<branch> by git fetch.

Was this helpful?