Skip to main content

Component Not Selectable

You may encounter a situation where your component renders correctly in the preview, but you cannot click to select it in Weaverse Studio. The section appears in the left panel’s section list but clicking it or clicking directly on the preview does nothing — the settings panel stays empty. This is commonly referred to as a “ghost section” and Studio will display a warning on the affected item:
Weaverse Studio showing a warning icon on a component that is not selectable, with a hover popup explaining the issue

Why This Happens

Weaverse Studio identifies components in the preview by looking for a data-wv-id attribute on the DOM element. This attribute is automatically included in the props that Weaverse passes to your component. There are two main causes for this issue:
  1. Props not spread correctly — the data-wv-id attribute is in the props but never reaches the DOM
  2. Component returns null — no DOM element is rendered at all, so there’s nothing for Studio to find

How to Fix

Cause 1: Props Not Spread Correctly

When your component doesn’t spread the remaining props (...rest) onto its root DOM element, the data-wv-id attribute never reaches the DOM. Studio can see the component exists in the data layer, but it cannot find the corresponding element in the preview.
1

Destructure your custom props from the rest

Separate your component-specific props and children from everything else using the spread operator:
function MySection(props: MySectionProps) {
  const { heading, description, children, ...rest } = props;
  // ...
}
2

Spread the remaining props onto the root DOM element

Pass ...rest to the outermost HTML element. This ensures data-wv-id and all other Weaverse-internal attributes reach the DOM:
function MySection(props: MySectionProps) {
  const { heading, description, children, ...rest } = props;

  return (
    <section {...rest}>
      <h2>{heading}</h2>
      <p>{description}</p>
      {children}
    </section>
  );
}
3

Restart the development server

After making changes, restart your dev server to pick up the updated component:
npm run dev

Before & After

// app/sections/my-section/index.tsx
function MySection(props: MySectionProps) {
  // Only destructured custom props — rest of props are lost
  const { heading, description } = props;

  return (
    // data-wv-id and other Weaverse props are lost
    <section>
      <h2>{heading}</h2>
      <p>{description}</p>
    </section>
  );
}
If you’re using forwardRef, the same rule applies — spread ...rest and pass the ref:
import { forwardRef } from "react";

const MySection = forwardRef<HTMLElement, MySectionProps>((props, ref) => {
  const { heading, description, children, ...rest } = props;

  return (
    <section ref={ref} {...rest}>
      <h2>{heading}</h2>
      <p>{description}</p>
      {children}
    </section>
  );
});

export default MySection;
With React 19, forwardRef becomes less necessary. If you spread all props (including ref) onto the root element, Weaverse can detect the DOM element automatically. See the Weaverse Component guide for more details.

Cause 2: Component Returns null

If your component conditionally returns null, no DOM element is rendered at all. Without a DOM element, there’s no place for the data-wv-id attribute to exist, and Studio cannot locate or select the component. This commonly happens when components guard against missing data.
1

Find the early return

Look for any return null statements in your component, especially conditional guards at the top:
function FeaturedProduct(props: FeaturedProductProps) {
  const { loaderData, children, ...rest } = props;
  const product = loaderData?.product;

  // This is the problem — no DOM element when product is missing
  if (!product) {
    return null;
  }

  return (
    <section {...rest}>
      <h2>{product.title}</h2>
      {children}
    </section>
  );
}
2

Always render a root element with props spread

Replace the return null with an empty self-closing element that spreads ...rest:
function FeaturedProduct(props: FeaturedProductProps) {
  const { loaderData, children, ...rest } = props;
  const product = loaderData?.product;

  if (!product) {
    return <section {...rest} />;
  }

  return (
    <section {...rest}>
      <h2>{product.title}</h2>
      {children}
    </section>
  );
}
3

Restart the development server

After making changes, restart your dev server to pick up the updated component:
npm run dev

Before & After

function FeaturedProduct(props: FeaturedProductProps) {
  const { loaderData, children, ...rest } = props;
  const product = loaderData?.product;

  if (!product) return null;

  return (
    <section {...rest}>
      <h2>{product.title}</h2>
      {children}
    </section>
  );
}
Never return null from a Weaverse component. Always render at least a root element with {...rest} so Studio can identify and select it. Use conditional rendering inside the root element instead.

Common Patterns That Cause This

Watch out for any conditional return before the root element:
// All of these prevent the root element from rendering
if (!data) return null;
if (items.length === 0) return null;
if (!isEnabled) return null;
if (loading) return null;
Instead, move the condition inside the root element:
return (
  <section {...rest}>
    {!data ? <Placeholder /> : <Content data={data} />}
  </section>
);
This is a subtler version of the same problem:
// Wrong
return isVisible ? (
  <section {...rest}>{children}</section>
) : null;

// Correct
return (
  <section {...rest}>
    {isVisible ? children : null}
  </section>
);

Checklist

If a component is not selectable in Studio, verify:
  • The component never returns null — return an empty element with {...rest} instead
  • The component destructures custom props and captures ...rest
  • ...rest is spread onto the root DOM element
  • The development server has been restarted after changes