plasmic Documentation

Visual builder for React. Build apps, websites, and content. Integrate with your codebase.

7,124 stars TypeScript TypeScript 9 files ยท ~12,629 tokens #builder#cms#design#design-systems#design-tools#drag-drop#frontend#gatsby
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy plasmic on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name plasmic -p 5432:5432 --restart always postgres:15
Configured Services & Ports
โš™๏ธ plasmic-db
image: postgres:15
๐Ÿ”Œ Ports: 5432:5432
โš™๏ธ plasmic-wab
image: build locally
๐Ÿ”Œ Ports: 3003:3003, 3004:3004, 3005:3005, 9229:9229
docker-compose.yml
yaml (docker-compose.yml)
services:
  # PostgreSQL database
  plasmic-db:
    image: postgres:15
    restart: always
    environment:
      - POSTGRES_PASSWORD=SEKRET
      - POSTGRES_USER=wab
      - POSTGRES_DB=wab
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./platform/wab/tools/docker-dev/db-setup.bash:/docker-entrypoint-initdb.d/db-setup.sh
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U wab"]
      interval: 5s
      timeout: 10s
      retries: 5

  # Plasmic WAB (Web App Builder)
  plasmic-wab:
    build:
      context: .
      dockerfile: ./platform/wab/tools/docker-dev/Dockerfile.dev.aio
    command: # Override the default dockerfile startup command here for debugging purpose
      - |
        jq '(.host = "plasmic-db") | (.password //= "SEKRET")' ormconfig.json > tmp.json && mv tmp.json ormconfig.json && \
        pnpm typeorm migration:run && \
        pnpm seed && \
        cd /plasmic && \
        pnpm dev
    volumes:
      # 1. Mount your local source code into the container
      - .:/plasmic
      # 2. Use named volumes to preserve built container files from being overwritten by unbuilt local code
      # --- Node Modules (Existing) ---
      - plasmic_node_modules:/plasmic/node_modules
      # Has the pnpm store that platform/* node_modules symlink into.
      - plasmic_platform_node_modules:/plasmic/platform/node_modules
      - plasmic_wab_node_modules:/plasmic/platform/wab/node_modules
      - plasmic_sub_node_modules:/plasmic/platform/sub/node_modules
      - plasmic_canvas_node_modules:/plasmic/platform/canvas-packages/node_modules
      - plasmic_live_frame_node_modules:/plasmic/platform/live-frame/node_modules
      - plasmic_react_web_bundle_node_modules:/plasmic/platform/react-web-bundle/node_modules
      - plasmic_loader_bundle_env_node_modules:/plasmic/platform/loader-bundle-env/node_modules
      - plasmic_loader_html_hydrate_node_modules:/plasmic/platform/loader-html-hydrate/node_modules

      # --- Generated files ---
      - plasmic_public:/plasmic/platform/wab/public
      - plasmic_wab_client:/plasmic/platform/wab/src/wab/client
      - plasmic_wab_gen:/plasmic/platform/wab/src/wab/gen
      - plasmic_wab_shared_model:/plasmic/platform/wab/src/wab/shared/model
      - plasmic_generated_styles:/plasmic/platform/wab/src/wab/styles

      # --- Build Caches ---
      - plasmic_playwright_cache:/home/plasmic/.cache/ms-playwright
    ports:
      - "3003:3003"
      - "3004:3004"
      - "3005:3005"
      - "9229:9229"
    depends_on:
      plasmic-db:
        condition: service_healthy
    environment:
      - DATABASE_URI=postgres://wab:SEKRET@plasmic-db:5432/wab
      - WAB_DBNAME=plasmic-db
      - WAB_DBPASSWORD=SEKRET
      - NODE_ENV=development
      - POSTGRES_USER=wab
      # This environment variable makes file watching more reliable.
      - CHOKIDAR_USEPOLLING=true
    tty: true
    stdin_open: true

volumes:
  postgres_data:
  plasmic_node_modules:
  plasmic_platform_node_modules:
  plasmic_wab_node_modules:
  plasmic_sub_node_modules:
  plasmic_canvas_node_modules:
  plasmic_live_frame_node_modules:
  plasmic_react_web_bundle_node_modules:
  plasmic_loader_bundle_env_node_modules:
  plasmic_loader_html_hydrate_node_modules:
  plasmic_playwright_cache:
  plasmic_generated_styles:
  plasmic_public:
  plasmic_wab_client:
  plasmic_wab_gen:
  plasmic_wab_shared_model:

Technical Documentation: plasmicapp/plasmic

โ„น๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference

Overview (README.md)

Plasmic

The open-source visual builder for your codebase.

 

See Plasmic in action

What is Plasmic?

Plasmic is a visual builder for the web.

It enables rapidly designing and building applications and websites--code optional.

Main use cases:

  • Content management: let marketing drag/drop your React components to build landing pages in your Next.js website, with design guardrails

  • Applications: let developers and technical users quickly build internal tools, client portals, and business software

  • Website builder and design tool that doesnโ€™t limit you to some built-in ecommerce platform, CMS, or hosting

Plasmic is powerful, with a deep feature set that scales to complex projects.
And with codebase integration, it removes the ceiling typically associated with low-code tools.

What makes Plasmic special?

Plasmic combines some seemingly disparate genres:

  • Webflow, Wordpress and other site builders
  • Retool and other tool builders
  • Glide and no-code app builders
  • Contentful and other CMSes

Today these are different tools to specialize in, but the line between, say, a website and an application is blurry (consider an ecommerce storefront with user logins). With the right foundations, we think these can be unifiedโ€”Plasmicโ€™s UI can adapt to different levels of control for different personas/tasks.

But more importantly, unlike existing tools, Plasmic integrates with codebases. This is critical to making low-code scale past the complexity ceiling that all such tools (including Plasmic) have. You can drag and drop existing complex React components, and you can visually create new UIs/components within traditionally-coded applications, seamlessly weaving code and no-code.

Some feature highlights:

  • Full design freedom and speedy modern design tool UX.
  • Integrate with codebases to drag/drop existing React components, publish screens into existing applications, and extend/customize Plasmic Studio.
  • Create rich stateful interactions and behavior.
  • Connect with arbitrary data source and backend integrations.
  • Powerful abstractions like components, variants, slots, composable state management, and more that promote composition and let you build and maintain at scale.
  • Customizable headless design system components powered by react-aria.
  • Content creator mode: give specific collaborators a more simplified editing experience with design guardrails.
  • Open integrations: choose your own CMS, ecommerce platform, hosting provider, and more.
  • Deep collaboration with multiplayer, branching, cross-project imports, and multi-workspace organizations.
  • Import designs from Figma, translating its proprietary vector document format into DOM/CSS.
  • Page performance and high-quality codegen. Supports static site generation, automatic image optimization, layout shift reduction, and more.
  • Deploy/host/export anywhere, including Vercel, Netlify, or any hosting provider.
  • End-user auth and permissions, including RBAC and user-scoped permissions.
  • Open-source platform that you can always fork and control.

Learn more on our website and our docs. Or check out comparisons of Plasmic vs other tools.

How do I integrate Plasmic as a CMS?

This is one popular use case of Plasmic.

Step 1. Install Plasmic into your codebase (exact package depends on your framework).

text
npm install @plasmicapp/loader-nextjs

Step 2 (optional). Make components from your app or design system available for drag-and-drop in the visual editor:

tsx
// Take any component from your app or design system...
export default function HeroSection({ children }) {
  return <div className="hero-section">{children}</div>;
}

// ...and make it available for drag-and-drop, along with any props you want to
// expose.
PLASMIC.registerComponent(HeroSection, {
  props: {
    children: "slot",
  },
});

Step 3. Add placeholders that render pages/components made in the visual editor anywhere in your app:

tsx
// pages/index.tsx

import {
  PlasmicComponent,
  PlasmicRootProvider,
} from "@plasmicapp/loader-nextjs";
import { PLASMIC } from "../plasmic-init";

// Here we fetch dynamically on the client, but you can also fetch and render
// components server-side in SSG/SSR frameworks, such as via getStaticProps
// in Next.js.
export default function IndexPage() {
  return (
    <PlasmicRootProvider plasmic={PLASMIC}>
      <PlasmicComponent component="Summer22LandingPage" />
    </PlasmicRootProvider>
  );
}

Step 4. Non-developers (or developers!) can now create new pages, sections, or components that ship directly into the app/website.

Step 5. When you hit Publish, changes get picked up via webhooks that trigger rebuilds,
or more specific mechanisms such as incremental static revalidation or dynamic fetching from the Plasmic CDN.

Who uses Plasmic?

Plasmic is used by companies ranging from Fortune 500s to boutique brands to solo makers.
It's used for websites ranging from headless commerce storefronts to marketing websites to logged-in app content.

Check out the Case Studies and Community Showcase.

How does Plasmic work?

How codebase integration works

Note: you do not need to integrate Plasmic with a codebase.
This is core to using Plasmic as a CMS, but you can build complete apps and websites without this, entirely within Plasmic.

Read the full technical overview.

Bring your own React components

You can register your own arbitrary custom React components for use as building blocks within Plasmic Studio.
Learn more about code components.

Codegen

Besides the Headless API, you can also generate React code into your codebase.
This is a powerful way to use Plasmic as a UI builder for creating rich interactive web applicationsโ€”one example of this is Plasmic Studio itself.
See the application development tutorials to learn more.

Note on versioning

One common issue we see is mismatched or duplicate versions of packages.

@plasmicapp packages can depend on each other.
Each package always has an exact version of its @plasmicapp dependencies.
This is because we want to ensure that all packages are always in sync, and that we don't end up with a mismatched set of packages.

Packages like @plasmicapp/host must also be deduped, since functionality such as registerComponent make use of globals and side effects, so with multiple versions you could end up using the wrong "instance" of this package.
Additionally, types can be tightly coupled across multiple packages.

Unfortunately, npm and yarn make it easy for you to end up with mismatched versions and duplicate versions of packages.
Use the npm list command to ensure that you have unique deduped versions of packages.
Furthermore, issues can be "sticky," since npm/yarn are stateful.
At times, you may need to rely on npm dedupe, or removing and reinstalling Plasmic packages (including @plasmicpkgs packages), resetting package-lock.json/yarn.lock, in order to unwedge npm/yarn.

@plasmicpkgs (the built-in code component packages) have @plasmicapp packages as peer dependencies,
and these specify ranges rather than exact versions--this is to offer some flexibility for developers to use the core package versions they need, while still using @plasmicpkgs.

Note: exact versioning does not imply that every package increments versions for every release.
Packages are only incremented if they or their dependencies have changed.
Incrementing versions is done automatically when our deployment scripts run lerna version patch --exact...,
which detects whether a package has changed since its last git-tagged release.
Internal dependencies and devDependencies are declared as workspace:*, which pnpm publish replaces with the exact version at pack time.

Contributing ๐Ÿš€

Please see CONTRIBUTING.md.

Get help and join our community

Our docs and our community forum and Slack with 3000+ members are the best places to get help with Plasmic.

For support from the Plasmic team, please use the forum.
The forum is also easily searchable for all previously asked questions and discussions.

Both the docs and forum are indexed by search engines!
Search both by including โ€œplasmicโ€ in your query.

License

All content outside of platform/ is licensed under the MIT license--see LICENSE.md.

platform/ is licensed under the AGPL--see LICENSE.platform.md.

Contributors โค๏ธ

Thanks to all the people who make Plasmic!


Contributing Platform 00 Getting Started (`docs/contributing/platform/00-getting-started.md`)

Developing Plasmic Studio: getting started

Database setup

Plasmic uses PostgreSQL (v15) as a database. We highly recommend setting postgres through docker (see docker-compose.yml for details). If you follow the Docker setup guide -- database will be created for you automatically.
If you prefer the manual application set up just run docker-compose up -d --no-deps plasmic-db in your terminal to build and launch the postgresql instance.

OS-specific instructions

Mac OS X

This section applies only if you install postgres manually, not through docker.

Update the version numbers for Postgresql in the following depending on what's latest!

text
{ sed 's/#.*//' | while read line; do sudo port install -b $line; done ; } << "EOF"
curl-ca-bundle
postgresql15
postgresql15-server
hadolint # for Dockerfile checking
EOF

# Follow the instructions printed during the postgresql-server installation:
sudo port select postgresql postgresql15
sudo mkdir -p /opt/local/var/db/postgresql15/defaultdb
sudo chown postgres:postgres /opt/local/var/db/postgresql15/defaultdb
sudo su postgres -c 'cd /opt/local/var/db/postgresql15 && /opt/local/lib/postgresql15/bin/initdb -D /opt/local/var/db/postgresql15/defaultdb
sudo port load postgresql15-server

(Your life will be much easier if you use macports. Don't use brew for installing PG.)

In order for sudo -u postgres psql to work without error messages, you should chmod 755 ~.

Ubuntu 18.04+

  • Install OS packages:

    text
    apt update
    apt install build-essential python3 python3-pip virtualenvwrapper postgresql postgresql-contrib wget screen
  • You may also need to increase your max watched file limit (see issue):

    text
    echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p

asdf

asdf (https://asdf-vm.com/) manages multiple versions of runtimes/tools that we use, such as node and python.
This can be useful if you need to work in multiple environments on the same machine.

Versions are kept in version control in .tool-versions and .envrc.

  1. Install asdf: https://asdf-vm.com/guide/getting-started.html
  2. Install plugins:
    text
    asdf plugin add nodejs
    asdf plugin add python
    asdf plugin add direnv
  3. Run asdf install from the root directory (and any dir with .tool-versions).
  4. In the root directory, run: direnv allow.
  5. Check that the installed tools work and have the correct version.
    bash
    node --version
    python --version

Notes:

  • You want asdf for python instead of using your Mac's system python because
    • in the latest MacOS 12.6 (which suddenly switches to python 3.9) building binary packages within certain environments like the pre-commit pyenv is broken, unless maybe you install Xcode.
    • we have more consistent control across platforms of which python we are relying on, which can have subtle effects on the chances of success of npm gyp builds. Plus, no more sudden switching python 3.8 to 3.9 (for ex) just because you updated MacOS!
  • You want asdf-direnv so that:
    • you get local virtualenv's managed by direnv (what's called "layouts" in direnv) - these local envs are not handled by asdf.
    • things like node --various-node-flags $(which pnpm) work as expected (used in various scripts).
    • plus the general benefits of avoiding shims.

Docker setup

By default, the source code is mounted as a shared volume with the host. The node modules are mounted separately, each having their own volume, in order to cache dependency installation and make sure we don't pollute the shared volume with the built artifacts.

This setup is quite heavy on CPU/RAM consumption, make sure you have at least 8GB of RAM available for the application container.

Launching the app through docker takes just a single command from the root of the repository:

text
docker-compose up -d

It will automatically spin up database, seed it, perform all the migrations, build all the source code needed for the studio to operate properly, and launch the server.

Manual setup (suggested)

Before proceeding, make sure you have configured your database and git hooks.

1. Environment variables

Make sure the root of your project and ./platform/wab folder contain the following .env files:

text
DATABASE_URI=postgres://wab:SEKRET@localhost:5432/wab
WAB_DBNAME=plasmic-db
WAB_DBPASSWORD=SEKRET
NODE_ENV=development

2. Installing dependencies

Run pnpm install in the root folder, then pnpm install in ./platform (one pnpm workspace covering wab, canvas-packages, loader-bundle-env and loader-html-hydrate)

Configuring Git and Git hooks

The root pnpm install also sets up the repository's Git hooks through Husky. In a fresh Git worktree, run it at the worktree root before your first commit. The generated, gitignored .husky/_ directory is absent until the install runs, so Git silently skips the hooks.

3. Seeding the database

In the ./platform/wab run:

text
pnpm seed

4. Application setup

In the project root directory, run:

text
pnpm setup-all && pnpm bootstrap

5. Starting dev servers

Run all servers (frontend, backend, and watchers):

text
pnpm dev

Using the app

The app runs on http://localhost:3003/.

If you have freshly reset the DB, the following users will be created for you
(see seed script for details):

The password for these accounts is !53kr3tz!.

WARNING: Avoid testing with the [email protected] user.
By default, the admin.example.com domain is considered an admin and has
elevated privileges (e.g. access to all teams, workspaces, projects, etc).
For most development purposes, use a normal user such as [email protected].

Troubleshooting

Executable `hadolint` not found

Install hadolint if you do any changes to the docker configs

tools/dev.bash: line 3: concurrently: command not found

If your /platform/wab/tools/dev.bash throws an error about concurrently not being a function, re-write it as npx concurrently

Next steps

Dive deeper into the tooling and configuration in 01-config-tooling.md.


Contributing Corporate Cla (`docs/contributing/corporate-cla.md`)

In order to clarify the intellectual property license granted with Contributions from any person or entity, Plasmic Inc ("Plasmic") must have a Contributor License Agreement (CLA) on file that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of Plasmic and its users; it does not change your rights to use your own Contributions for any other purpose.

This version of the Agreement allows an entity (the "Corporation") to submit Contributions to Plasmic, to authorize Contributions submitted by its designated employees to Plasmic, and to grant copyright and patent licenses thereto.

You accept and agree to the following terms and conditions for Your present and future Contributions submitted to Plasmic. Except for the license granted herein to Plasmic and recipients of software distributed by Plasmic, You reserve all right, title, and interest in and to Your Contributions.

  1. Definitions.

    "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with Plasmic. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.

    "Contribution" shall mean the code, documentation or any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to Plasmic for inclusion in, or documentation of, any of the products owned or managed by Plasmic (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to Plasmic or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Plasmic for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."

  2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to Plasmic and to recipients of software distributed by Plasmic a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.

  3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to Plasmic and to recipients of software distributed by Plasmic a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.

  4. You represent that You are legally entitled to grant the above license. You represent further that each employee of the Corporation designated by You is authorized to submit Contributions on behalf of the Corporation.

  5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others).

  6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.

  7. Should You wish to submit work that is not Your original creation, You may submit it to Plasmic separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]".

  8. It is your responsibility to notify Plasmic when any change is required to the list of designated employees authorized to submit Contributions on behalf of the Corporation, or to the Corporation's Point of Contact with Plasmic.


Contributing Individual Cla (`docs/contributing/individual-cla.md`)

In order to clarify the intellectual property license granted with Contributions from any person or entity, Plasmic Inc ("Plasmic") must have a Contributor License Agreement ("CLA") on file that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of Plasmic; it does not change your rights to use your own Contributions for any other purpose.

You accept and agree to the following terms and conditions for Your present and future Contributions submitted to Plasmic. Except for the license granted herein to Plasmic and recipients of software distributed by Plasmic, You reserve all right, title, and interest in and to Your Contributions.

  1. Definitions.

    "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with Plasmic. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.

    "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to Plasmic for inclusion in, or documentation of, any of the products owned or managed by Plasmic (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to Plasmic or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Plasmic for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."

  2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to Plasmic and to recipients of software distributed by Plasmic a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.

  3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to Plasmic and to recipients of software distributed by Plasmic a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.

  4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to Plasmic, or that your employer has executed a separate Corporate CLA with Plasmic.

  5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions.

  6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.

  7. Should You wish to submit work that is not Your original creation, You may submit it to Plasmic separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]".

  8. You agree to notify Plasmic of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect.


Contributing Platform 01 Config Tooling (`docs/contributing/platform/01-config-tooling.md`)

Developing Plasmic Studio: config and tooling guide

Config (optional)

Write something like this in ~/.plasmic/secrets.json:

text
{
  "encryptionKey": "dummykey",
  "google": {
    "clientId": "SEE_GOOGLE_INSTRUCTIONS_BELOW",
    "clientSecret": "SEE_GOOGLE_INSTRUCTIONS_BELOW"
  },
  "resendApiKey": "SET_THIS_TO_RESEND_API_KEY",
  "smtpAuth": {
    "user": "SET_THIS_TO_SMTP_USER",
    "pass": "SET_THIS_TO_SMTP_KEY"
  },
  "segmentWriteKey": "ignorethis"
}

You'll also need ~/.aws/credentials, since various parts such as codegen/publish and Figma import use S3.

Database

Setup DB

On project root directory, make sure the Postgresql server is running, and run:

text
pnpm db:setup
pnpm db:reset # specify no_sudo=1 if `sudo -u postgres psql` doesn't work

Reset DB State

If you ever want to, you can reset the DB state by running (on project root):

text
pnpm db:reset # specify no_sudo=1 if `sudo -u postgres psql` doesn't work

Important: remember clearing your browser cookies and restart any running servers.

Running Servers

Running Servers using screen

From platform/wab, run all servers in screens:

text
bash tools/start.bash

If you'd like to disable type-checking for faster incremental dev-server builds,
use:

text
NO_TYPECHECK=1 bash tools/start.bash

After start.bash, you'll automatically get three panes viewing various terminals, each one running some subset of procseses.

(Experimental) Running on different ports

If you'd like to run on an alternate backend (app server) port:

text
BACKEND_PORT=3007 bash tools/start.bash

If you'd like to run on an alternate frontend (webpack dev server) port:

text
PORT=3006 bash tools/start.bash

If you'd like to run on an alternate database name:

text
WAB_DBNAME=altwab bash tools/start.bash

(Incomplete) Running Servers manually

(This just documents running things in wab, but you must also run things outside of wab.)

In wab folder

Run backend

text
pnpm backend

Run frontend client dev server

text
pnpm start

Run host client, just a proxy on port 3005 to the frontend

text
pnpm host-server

Running Servers using pm2

You can also use pm2 to manage all the server processes in dev environment.
First, initialize the shell as:

text
workon wab
. ~/.node/*/bin/activate

Install pm2 globally so you can use pm2 rather than "pnpm pm2"

text
npm install pm2

To start all processes, just

text
cd wab
pm2 start pm2-dev.config.js

To stop all processes,

text
pm2 stop all

To delete all processes,

text
pm2 delete all

To inspect logs,

text
pm2 logs

Refer to https://pm2.keymetrics.io/docs/usage/quick-start/ for more usage information.

When pulling codebase

Whenever you fetch the latest changes, most of the time, you just need to run:

text
pnpm install
make
# restart node server
# restart webpack, once in a blue moon

But if something is still going wrong, try:

text
pnpm setup
# restart node server
# restart webpack, once in a blue moon

If the above doesn't fix the issue, try again but running pnpm setup-all instead.

SVG Icons

For the in-flux SVG icons, install the icon fonts from https://github.com/keremciu/font-bundles

Plume special package

To make sure your local database contains the latest version of the Plume
package so that you can create components from Plume templates, run:

text
pnpm plume:dev update

If you don't do so, studio may show a NotFoundError when you open any new
project.

Testing

Run unit tests with:

text
bash tools/test.bash

Migrating DB/model bundle schema

To migrate bundles, create a new file in the bundle-migrations following the same format as existing files. Small example:

text
// wab/src/wab/server/bundle-migrations/XX-my-migration.ts

import { UnsafeBundle } from "../../shared/bundles";

export function migrate(bundle: UnsafeBundle) {
    for (const [k, v] of Object.entries(bundle.map)) {
        if (v.__type === "Rule") {
            v.values = v.values;
        }
    }
}

And that is!

If you want to revert, simply remove the file and then restart the app server. WARNING: this will occur in data loss. If you can create a new migration instead, do so!

In reality, you only have to worry about adding files and reverting files. Our deployment scripts will take care of the rest. Here's a brief explanation on how to do the changes in your local environment:

  • Migrating: add a new migration and restart the server.
  • Reverting: remove the migration and restart the server.

Migrating dev/test bundles

We have some local JSON bundles for development/test purposes, which you also need to migrate.

To migrate these, run:

bash
pnpm migrate-dev-bundles

This runs any necessary migrations according to the version stamp.

Then make sure you run the tests and update the snapshots.

NOTE: This will first do a git checkout on the file, resetting to a fresh checkout state! This lets you repeatedly test and run your migration script on the file.

Debugging Studio

Because Studio runs in a cross-origin iframe, debugging becomes a bit trickier.

In particular, the React Devtools Chrome extension will not work. However, you can run the standalone React Devtools Electron package.

Install and run react-devtools:

bash
npm install react-devtools
    react-devtools

Alternatively you can run it with npx:

bash
npx react-devtools

And now when you open up Studio with the devflag ?enableReactDevTools=true and it should auto-connect.
It should work for both dev server and prod.

Debugging Node Server

You can use IntelliJ/Webstorm.

Or use node --inspect to debug your node app using Chrome DevTools - just open about:inspect in Chrome as per
https://medium.com/@paul_irish/debugging-node-js-nightlies-with-chrome-devtools-7c4a1b95ae27.

Ant

We're opting to import all Ant styles wholesale and override their globals
in antd-overrides.less. This allows for live theming (no dev server restarts
necessary).

Maintaining dependencies

Check what dependencies are not used (or missing):

text
pnpm knip:deps

Check what needs to be updated:

text
pnpm outdated

Update the dependencies:

text
pnpm up --latest

This will upgrade everything. You can also try selectively upgrading individual
packages, but things get complicated with upgrading dependencies that are
also indirect dependencies of other dependencies.

Alternate configs

When pointing to a different DB, you currently have to make sure you locally
edit ormconfig.json (used by typeorm CLI) and set the WAB_DBNAME env var.

Audit licenses of dependencies

For node dependencies, do this from each project directory:

text
npx license-checker --csv --out license-checker.csv

For Python dependencies, do this from each project directory:

text
pip-licenses --from=mixed -f csv > pip-licenses.csv

Production Build

Run pnpm build to build client app for production. This takes a long time (>5m).

You can test out your built artifact with:

text
npm install local-web-server
  cd build/
  ws --spa index.html --rewrite '/api/(.*) -> http://localhost:3004/api/$1'

Then open http://localhost:8000.


Contributing Platform 02 Integrations (`docs/contributing/platform/02-integrations.md`)

Developing Plasmic Studio: third-party system integrations

Figma import

See Figma plugin setup.

Plasmic hosting on Vercel

To locally access a Plasmic-hosted project, run the hosting application from
platform/hosting:

bash
cd platform/hosting
pnpm dev

To run it against a local Studio project, temporarily hardcode the local project's
projectId, token, and wantToShowBadge in
platform/hosting/pages/_sites/[site]/[[...catchall]].tsx. Then set the API host:

bash
cd platform/hosting
NEXT_PUBLIC_PLASMIC_HOST=http://localhost:3003 pnpm dev

You can access a published project at http://localhost:3000/_sites/yourdomain.plasmic.run,
which is equivalent to yourdomain.plasmic.run.

Setting up Google SSO

For now, users who use SSO must have an email domain that matches an org's domain.

Each developer who works on Plasmic and wants to try out SSO will need to set up
a project on Google Developers Console.

  • Go to Google Developers Console
  • On the upper-left corner, you can see the Project you are using (next to three black hexagons).
    Click it, and in the dialog box, click "New Project" on the upper-right.
  • Name your project something like "Plasmic Yang Dev"
  • Click on the Credentials tab on the left sidebar.
  • First click on the "OAuth consent screen". Fill in the required fields, and for scopes,
    make sure "email", "profile", "openid" are included (should be by default).
  • Next, click on the "Credentials tab"
  • Save the client ID and secret in your ~/.plasmic/secrets.json. It should look like:
    json
    {
      "encryptionKey": "this is a secret DB encryption key",
      "google": {
        "clientId": "YOUR CLIENT ID",
        "clientSecret": "YOUR CLIENT SECRET"
      }
    }

Setting up GitHub App

  • Sign in to GitHub with your personal account and go to
    GitHub Developer Settings.

  • Create a new GitHub App with the following information:

    • Homepage URL: http://localhost:3003
    • Callback URL: http://localhost:3003/github/callback
    • Uncheck "Expire user authorization tokens"
    • Check "Request user authorization (OAuth) during installation"
    • Deactivate webhook
    • Repository permissions:
      • Actions: Read & write
      • Administration: Read & write
      • Contents: Read & write
      • Pages: Read & write
      • Pull requests: Read & write
      • Workflows: Read & write
  • Click on "Edit" in the app you created and generate/store a client secret
    and a private key.

  • Fill the following fields in your ~/.plasmic/secrets.json:

    json
    {
      "github": {
        "appId": "<APP_ID>",
        "privateKey": "-----BEGIN RSA PRIVATE KEY-----\n<YOUR_KEY>...",
        "oauth": {
          "clientId": "<CLIENT_ID>",
          "clientSecret": "<CLIENT_SECRET>"
        }
      }
    }

    Note that the private key must have its line breaks replaced by "\n".

  • Edit the devflags (in your local storage) changing githubClientId and
    githubAppName to your app client ID and kebab-cased name (copy it from the
    app URL), then reload studio for changes to take effect.

Developing/debugging GitHub integration

Here are various tips and tools for localhost development.

Set an externally facing URL

GitHub Actions needs to call your app server to perform syncs, so use a service
like localhost.run and set your externalBaseUrl devflag to point to your
resulting temp URL.

Use toolchain debug flags

We internally use our own toolchain including create-plasmic-app,
@plasmicapp/cli, @plasmicapp/loader, etc. For local-only testing in a pnpm
project, use the local-link setup/build/teardown workflow documented in
CONTRIBUTING.md. It uses temporary file: overrides
without changing the package versions declared by the consuming project. You
can also use the various debug flags for those packages like
PLASMIC_DEFAULT_HOST.

For example, to have the CLI/PlasmicLoader point to your own localhost server:

text
PLASMIC_DEFAULT_HOST=http://localhost:3003 pnpm backend

This is ignored by the app server but consumed by CLI/PlasmicLoader.

See the various READMEs on those projects for relevant debug flags.

Publish the toolchain first

The CLI/PlasmicLoader does eventually get run on GitHub Actions runners, so the "easiest" way to test things for now is to publish first.

Potentially, you can also amend the resulting package.json (in the repo that gets pushed to GitHub) to point to some alternate package source, but I haven't done/needed this yet.

Setting up billing (Stripe)

To test billing locally, add Stripe test secret key (https://dashboard.stripe.com/test/apikeys) to secrets.json:

json
{
  "stripe": {
    "secretKey": "<PASTE_SECRET_KEY_HERE>"
  }
}

Setting up Google Sheets

Have a GCloud account, Sheets API enabled, OAuth consent screen.

Create an OAuth client.

Save into secrets.json:

json
{
  "google-sheets": {
    "clientId": "<CLIENT_ID>",
    "clientSecret": "<CLIENT_SECRET>"
  }
}

Contributing Platform 03 Figma (`docs/contributing/platform/03-figma.md`)

Figma plugin setup

The plugin copies a clipId and uploads selected layers through a web bridge (figma-plugin-web) to PUT /api/v1/clip/:clipId. Studio fetches the data on paste with GET /api/v1/clip/:clipId.

Use the production plugin

For internal development, use the published plugin if your Studio app server has authorized read access to production clip storage. Set CLIP_BUCKET, S3_ENDPOINT, and AWS credentials to match that storage, then restart the server. Having ~/.aws/credentials alone does not grant access.

For separate storage, configure the bridge and exporter below.

Configure storage

Set these on the Studio app server:

sh
CLIP_BUCKET=your-figma-clips
# For S3-compatible storage:
# S3_ENDPOINT=https://s3.example.com

Use a private bucket and AWS credentials with GetObject and PutObject access. Allow multipart operations for large uploads. No public
bucket access or bucket CORS is needed. CLIP_BUCKET defaults to plasmic-clips, S3_ENDPOINT also affects other app server storage clients. The clip client doesn't force path-style addressing.

Clip routes don't need Studio auth or CSRF, anyone with a clip ID can retrieve it. Remember to set an expiration policy.

Serve the web bridge

Use the repositoryโ€™s Node version and each packageโ€™s pinned pnpm version.
From the repository root:

sh
cd platform/figma-plugin-web
pnpm install --frozen-lockfile
PUBLIC_URL=/figma-plugin-app NODE_OPTIONS=--openssl-legacy-provider SKIP_PREFLIGHT_CHECK=true pnpm build

The flags are for Webpack 4 on Node 24 and Create React Appโ€™s ancestor dependency check. Serve build/ at https://studio.example.com/figma-plugin-app/, on the same origin as the Studio API. The bridge uses relative /api/v1/... URLs.

Build the exporter

In platform/figma-plugin-plasmic-exporter:

  1. Set iframe src in src/ui/components/App.tsx to your URL, e.g. https://studio.example.com/figma-plugin-app/.

  2. Set networkAccess.allowedDomains in manifest.json to ["https://studio.example.com"]. For local development, use devAllowedDomains with HTTPS origin and port (Figma manifest reference).

  3. Build:

    sh
    pnpm install --frozen-lockfile
    NODE_OPTIONS=--openssl-legacy-provider pnpm build

In Figma desktop, choose Plugins โ†’ Development โ†’ Import plugin from manifestโ€ฆ and select this packageโ€™s manifest.json. Run that plugin, select layers, click Export selected layers to clipboard, wait for completion, and paste into Studio. Rebuild and relaunch after source changes, and distribute manifest.jsonand dist/ together.

Troubleshoot

  • Export hangs: check bridge assets, HTTPS, iframe URL, manifest allowlist, framing headers, and login redirects in Figmaโ€™s developer console.
  • Upload fails: check the PUT response and app logs for storage permissions, routing, or request size limits.
  • Paste cannot find the clip: verify the pluginโ€™s destination and Studioโ€™s bucket/endpoint match. The published plugin uses production storage.

Sources: bridge,
clip API,
paste handler.


Contributing Platform Dev Notes (`docs/contributing/platform/dev-notes.txt`)

React Gotchas

  • For some reason, if a component crashes on the first render (within the
    ViewInitializer's first setState({viewCtx})), this causes some very
    confusing issues with attempting to reload the base-pkg and refresh the app
    again from the top. This might manifest as an attempt to render
    foreign components and then not finding the component in
    ForeignComponents.forValComponent(). I ran into this when e.g. enabling
    CreateTab rendering earlier (renders the gallery even when no selection is
    active, but there was a silly bug in CreateItem triggering the above symptoms)

Library Gotchas

  • Do not import from "jquery", always use the one from deps. They have
    different plugins, but more subtlely, they have different .data() stores.

WAB Gotchas

  • Tpls.ancestors doesn't do any filtering, but canvasCtx().valAncestors() filters
    out the valUserRoot! (Which is good since in both isolated component editing
    mode and normal page editing mode, they're more like sentinels.)

  • Tagger will actually tag the portaled div of a React Bootstrap Modal on first
    render (as seen in componentDidMount), but not later updates that re-show the
    div (componentDidUpdate just sees null). I'm not able to repro that initial
    tagging outside of WAB---see e.g. https://codesandbox.io/s/q8828q9p69. So
    don't rely on that initial tagging.

  • Currently, when you insert a Modal, you might have this val tree:

    ValComponent for Modal
    ValComponent for ModalBody
    ValTag for div

    which renders as these React components:

    CanvasElement
    Sub
    Modal
    ModalBody <-- Modal expects ModalBody to be its direct child
    Sup
    CanvasElement
    div

    Because of that parent-child requirement, we unfortunately can't interleave a
    CanvasElement, which would let us more easily associate the DOM node (with
    .getDOMNode() or .findDOMNode()) with that ValComponent (without overriding
    more bsRole settings---which may be a good thing to consider eventually).


Platform Wab Docs ICONS (`platform/wab/docs/ICONS.md`)

Icons

Studio icons are SVG React components generated by Plasmic, not hand-written
files. Most of them come from the [PlasmicKit] Icons project
(oT38tGyqov9SPWHpf3Y2Rf) and land in
src/wab/client/plasmic/plasmic_kit_icons/icons/PlasmicIcon__*.tsx. A few
older ones live under the other src/wab/client/plasmic/plasmic_kit_*/
directories.

Adding a new icon

  1. Check whether an existing icon works first. There are ~600 of them, so
    search src/wab/client/plasmic/plasmic_kit_icons/icons/ before drawing
    anything new.

  2. Add the SVG as an icon asset in the [PlasmicKit] Icons Plasmic project,
    following the conventions below.

  3. Sync it into the repo:

    text
    pnpm plasmic:sync

    This writes the generated component and registers it in plasmic.json.
    Commit both.

  4. Give it a semantic name in src/wab/client/icons.tsx rather than importing
    the generated PlasmicIcon__* module all over the codebase.

SVG conventions

New icons should match the existing set:

  • viewBox="0 0 24 24" (the set is drawn at 24ร—24, even though it renders at
    16px by default)
  • fill="none" on the <svg> element; draw with strokes, not fills
  • stroke="currentColor" so the icon inherits text color โ€” never hardcode a
    color
  • stroke-width="1.5"
  • stroke-linecap="round" and stroke-linejoin="round"
  • Prefer a single `` over many primitives
  • Keep the artwork inside the 24ร—24 box with a consistent margin instead of
    bleeding to the edges, so icons look the same weight when placed side by side

An existing icon that follows all of the above, useful as a reference:
src/wab/client/plasmic/plasmic_kit_icons/icons/PlasmicIcon__Braces.tsx.

Using an icon

Render icons through the Icon widget, which applies the standard sizing
(16px) and the custom-svg-icon class:

tsx
import { Icon } from "@/wab/client/components/widgets/Icon";
import BracesIcon from "@/wab/client/plasmic/plasmic_kit_icons/icons/PlasmicIcon__Braces";

<Icon icon={BracesIcon} />;

Pass size to override the default, and monochromeExempt for the rare icon
that should keep its own colors.


--- METRICS ---