How to attach state and events to Plasmic components?

Hi guys! I have been finding Plasmic truly amazing… partially because the tutorials are so helpful. I’ve barely used React but have been able to get a basic website up and running using custom code and components within a day. There’s no way this would have happened with any other product out there (and trust me, I tried a few!).

I had one issue that I couldn’t find in the docs: Is there a way to update Plasmic custom components within a Plasmic Page file so that they can “share” custom external variables and change states based on those variables?

e.g. Let’s say my Plasmic page has 3 custom Plasmic components that are all independently laid out on the Plasmic page (no parent/child relationships)… Component A, B, and C:

• Component A is a button. Every time it is clicked, a variable is incremented.
• Component B is a text box that displays the running total from clicking (A)
• Component C is a text box that starts hidden, but is unhidden when the variable from (A) reaches 10
This is pretty straightforward in a vanilla React app, but I want to use the Plasmic Component and the Plasmic Page, and just modify the child text property of B within it using the page file, and have component C appear using the Plasmic configs; that way the front-end users can style and move the Plasmic components around as they please and everything stays connected… Am I making sense?

Some of my boilerplate code for Component B and the page file…

// in components/ComponentB.jsx

function ComponentB(props, ref) {
  // return <PlasmicComponentB root={{ ref }} {...props}>{componentATotal}</PlasmicComponentB>;
  return <PlasmicComponentB root={{ ref }} {...props}>Placeholder</PlasmicComponentB>;
}

const ComponentB= React.forwardRef(ComponentB_);

export default ComponentB;
// in pages/index.jsx

function Home() {
  const [componentATotal] = getComponentATotal(); // componentATotal increments by 1 every time its component is clicked
  // a way to update PlasmicComponentB here?
  // if componentATotal >= 10 unhide PlasmicComponentC here?
  return <PlasmicIndex />;
}

export default Home;

Hello! You should be able to just do:

function Home() {
  const [total, setTotal] = useState(1);
  return <PlasmicIndex
    someInstanceOfComponentA={{
      onClick: () => setTotal(total+1)
    }}
    someInstanceOfComponentB={{
      children: total
    }}
    someInstanceOfComponentC={{
      wrap: node => total>=10 ? node : null
    }}
  />;
}

Hi Yang–Thank you for the quick reply. That sounds almost too easy! Maybe this is a silly question, but how do I gain access (and assign a variable) to the instance of each component, since they are all rendered through Plasmic automatically? i.e. I don’t manually create the instance for Component B… Plasmic does it for me on the page already.

You can just select the instance for component b in plasmic studio and rename it to whatever name you want, either from the blue selection box name tag or from the left tree sidebar. Those are the names I’m passing in as props to plasmicindex.

That did it! I can’t believe it’s this easy… Thanks for your patience with my beginner questions. I was struggling for a bit because I named it “ComponentB” in Plasmic, and it shows up that way, just fine. But when I do ‘plasmic sync,’ it actually pulls it down into the code as “componentB” … Not sure if that is intentional or a React thing to be aware of… But I’m naming everything with a lowercase letter to start from now on to be safe (and consistent)!

Ah yeah all elements are automatically lowerCamelCased to make the resulting API a bit more conventional

Hi Yang! Another probably easy question that’s killing me for the past few hours… How can I pass a variant to a named Component? I have a “big” and “small” variant for componentB…

The following doesn’t work… I also tried making a variant group called “state” to house them, and feeding that in, but no luck…

function Home() {
  const [total, setTotal] = useState(1);
  return <PlasmicIndex
    someInstanceOfComponentA={{
      onClick: () => setTotal(total+1)
    }}
    someInstanceOfComponentB={{
      variant: total >= 10 ? "big" : "small",
      children: total
    }}
    someInstanceOfComponentC={{
      wrap: node => total>=10 ? node : null
    }}
  />;
}

If the variants are standalone ‘toggle variants’, you can do:

someInstanceOfComponentB={{
  big: total >= 10,
  small: total < 10,
  children: total
}}

If they are part of a variant group called Size:

someInstanceOfComponentB={{
  size: total >= 10 ? 'big' : 'small',
  children: total
}}

Silly me… It was working using the second approach. But I am losing the text formatting from the variant because I reset “children” to the text, which looks like it removes the variant’s font styles… I think I need to just change the “text content” within children and preserve the style!

Is there a way to do that within here, or would I have to setup a new named component within it?

Or maybe this would need to use the “slot” feature where I set the text to be a slot, and then should have access to change it within here?

If you don’t already have a slot, you can just name the text element and override its children: someTextElement={{children: children}} . This will replace just the value and not the styles etc.

If you have a slot, pass it into that slot: mySlot={total} .

Thanks for sharing both approaches! The slot method is so clean–I will give that a try. Can’t wait to continue building… And going to try and sell it to our front-end UI artists soon! Thanks for all the help!!