Components: Blackbox Scheme
The “blackbox” codegen scheme for components aims to strictly separate files that are owned by Plasmic and the files that are owned by developers.  The Plasmic-owned files define the presentation, and the developer-owned files define the behavior.  Developers use the Plasmic-owned files as a “blackbox” library to help them render components as they are designed in Plasmic.

By our guiding principles, the blackbox scheme allows you to:

  • Update components with new designs by overwriting the Plasmic-owned files every time you run plasmic sync.  Since your edits are in developer-owned files, they will be safe, and will continue to work as long as the design didn’t introduce breaking changes.
  • Control component props by giving you total freedom in defining what props your component take, as long as you can map them to props that Plasmic-generated component understands.
  • Instrument components to do anything by providing a rich API for attaching and overriding behavior to the Plasmic-generated elements, and letting you use React hooks as you usually would to manage state and fetch data.

Generated files

For each component in the Plasmic project, we generate two sets of files:

  • Presentational Plasmic* components (e.g. plasmic/PlasmicButton.tsx)The files in the plasmic directory are a blackbox that know how to render the component.  Think of this as a utility library that you can call to render some UI.  It is owned by Plasmic, and shouldn’t be edited by you.  As you iterate on the design using Plasmic, these files will be updated when you run plasmic sync.
  • Wrapper components (e.g. Button.tsx)This source file is owned by you, and should be edited by you.  Here, you define the actual React component used throughout your application, and make use of the presentational Plasmic* component above to render the component, attaching the appropriate data bindings and event handlers as necessary.  Plasmic generates an empty scaffolding for this file the first time this component is synced, and never touches it again.

When you first plasmic sync your Button component, you might see a Button.tsx file that looks something like this:

// {tsx}
function Button(props: ButtonProps) {
  return <PlasmicButton {...props} />;
}

Here, the Button component is the actual React component that you would use to create a button throughout the rest of your application.  In turn, it calls the presentational PlasmicButton  component to do the actual rendering. 

We will talk about each of these in turn.  

Working with Plasmic* components

The “presentational library” components like PlasmicButton expose a purely presentational component that knows how to render and style a component exactly as designed in Plasmic, and nothing else.  These Plasmic*  components expose props that allows you to control which variants to activate, which slots to fill with what content, and which elements to instrument with real data or event handlers.  These files are owned by Plasmic and shouldn’t be edited by you; when the design for Button gets updated, plasmic sync will overwrite PlasmicButton.tsx to reflect the new designs.

Note that the Plasmic* components should only be used by its corresponding wrapper component (PlasmicButton should only be used by Button).  You should always use the wrapper component (Button) from the rest of your application.  In this way, the Plasmic* is completely encapsulated by the wrapper component; no one else needs to know about PlasmicButton except for Button.

There are four classes of props that you can pass to the Plasmic* components:

Variant props

Component variants are organized into groups.  For example, in a Button component, we may have a variant group role that includes primary and secondary variants, or a size group that includes small and large variants.  Often the groups are single-choice you only allow one variant per group to be active.  But sometimes it makes sense for them to be multi-choice — for example, a Button component may have a variant group withIcons that has options prefix and suffix, both of which can be true at the same time.

Each variant group is a prop on the Plasmic* presentational component.

  • For single-choice variant groups, you can pass in the name of the variant you want to activate, or undefined if none should be activated.
  • For multi-choice variant groups, you can pass in an array of variant names, or a object of variant names mapping to true or false.

Example:

// {tsx}
// Passing in literals to turn on `role` and `withIcons` variants
<PlasmicButton role="primary"  withIcons={["prefix", "suffix"]} />

// Turning on variants conditionally
<PlasmicButton 
  role={isPrimary() ? "primary" : isSecondary() ? "secondary" : undefined}
  withIcons={{
    prefix: hasPrefixIcon(),
    suffix: hasSuffixIcon()