> ## Documentation Index
> Fetch the complete documentation index at: https://docs.weaverse.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Component Not Selectable

> Fix components that appear in preview but cannot be selected or edited in Weaverse Studio.

# Component Not Selectable

You may notice an **info icon** next to a component in Weaverse Studio's outline panel. This indicates that Studio cannot locate the component's DOM element in the preview.

<Frame>
  <img src="https://cdn.shopify.com/s/files/1/0838/0052/3057/files/component-not-selectable.png?v=1775269173" alt="Weaverse Studio showing an info icon on a component that is not selectable in the preview" />
</Frame>

<Note>
  **Don't worry — your component is still fully editable.** You can always select it from the outline panel and modify its settings. The info icon simply means Studio can't highlight or select it by clicking directly on the preview.
</Note>

## 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 **three common causes** for this:

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
3. **Component is conditionally rendered** — a parent component controls whether this component appears in the DOM

## 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.

<Steps>
  <Step title="Destructure your custom props from the rest">
    Separate your component-specific props and `children` from everything else using the spread operator:

    ```tsx theme={null}
    function MySection(props: MySectionProps) {
      const { heading, description, children, ...rest } = props;
      // ...
    }
    ```
  </Step>

  <Step title="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:

    ```tsx theme={null}
    function MySection(props: MySectionProps) {
      const { heading, description, children, ...rest } = props;

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

  <Step title="Restart the development server">
    After making changes, restart your dev server to pick up the updated component:

    ```bash theme={null}
    npm run dev
    ```
  </Step>
</Steps>

#### Before & After

<CodeGroup>
  ```tsx Wrong highlight={4,8} theme={null}
  // 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>
    );
  }
  ```

  ```tsx Correct highlight={4,8} theme={null}
  // app/sections/my-section/index.tsx
  function MySection(props: MySectionProps) {
    // Capture everything else with ...rest
    const { heading, description, children, ...rest } = props;

    return (
      // Spread ...rest — this includes data-wv-id
      <section {...rest}>
        <h2>{heading}</h2>
        <p>{description}</p>
        {children}
      </section>
    );
  }
  ```
</CodeGroup>

If you're using `forwardRef`, the same rule applies — spread `...rest` and pass the `ref`:

```tsx theme={null}
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;
```

<Note>
  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](/development-guide/weaverse-component) for more details.
</Note>

***

### 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.

<Steps>
  <Step title="Find the early return">
    Look for any `return null` statements in your component, especially conditional guards at the top:

    ```tsx theme={null}
    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>
      );
    }
    ```
  </Step>

  <Step title="Always render a root element with props spread">
    Replace the `return null` with an empty self-closing element that spreads `...rest`:

    ```tsx theme={null}
    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>
      );
    }
    ```
  </Step>

  <Step title="Restart the development server">
    After making changes, restart your dev server to pick up the updated component:

    ```bash theme={null}
    npm run dev
    ```
  </Step>
</Steps>

#### Before & After

<CodeGroup>
  ```tsx Wrong highlight={5} theme={null}
  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>
    );
  }
  ```

  ```tsx Correct highlight={5} theme={null}
  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>
    );
  }
  ```
</CodeGroup>

<Tip>
  Avoid returning `null` from a Weaverse component. Render at least a root element with `{...rest}` so Studio can locate it in the preview. Use conditional rendering *inside* the root element instead.
</Tip>

#### Common Patterns That Cause This

<AccordionGroup>
  <Accordion title="Early returns for missing data" icon="database">
    Watch out for any conditional return before the root element:

    ```tsx theme={null}
    // 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:

    ```tsx theme={null}
    return (
      <section {...rest}>
        {!data ? <Placeholder /> : <Content data={data} />}
      </section>
    );
    ```
  </Accordion>

  <Accordion title="Returning null from a ternary" icon="code">
    This is a subtler version of the same problem:

    ```tsx theme={null}
    // Wrong
    return isVisible ? (
      <section {...rest}>{children}</section>
    ) : null;

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

***

### Cause 3: Component Is Conditionally Rendered

Sometimes the component itself is fine, but a **parent component** decides whether to render it based on some condition. When the condition is `false`, the child component is removed from the DOM entirely, and Studio can't find it in the preview.

This is standard [conditional rendering in React](https://react.dev/learn/conditional-rendering) — nothing is broken.

```tsx theme={null}
function ParentSection(props: ParentSectionProps) {
  const { showBanner, children, ...rest } = props;

  return (
    <section {...rest}>
      {/* When showBanner is false, BannerChild is not in the DOM */}
      {showBanner && children}
    </section>
  );
}
```

In this case, the child component will show an info icon in the outline panel whenever `showBanner` is `false`. **This is expected behavior** — when the parent renders the child again, it becomes selectable in the preview automatically.

<Tip>
  You don't need to fix this. It's a natural result of conditional rendering. The child component is always editable via the outline panel regardless of whether it's visible in the preview.
</Tip>

***

## Checklist

If a component shows an info icon in Studio, verify:

* The component destructures custom props and captures `...rest`
* `...rest` is spread onto the **root DOM element**
* The component **never returns `null`** — return an empty element with `{...rest}` instead
* If conditionally rendered by a parent — this is expected and safe, no action needed
* The development server has been restarted after changes

## Related Resources

* [Creating Components](/development-guide/creating-components)
* [Weaverse Component](/development-guide/weaverse-component)
* [Conditional Rendering (React docs)](https://react.dev/learn/conditional-rendering)
* [Preview Connection Errors](/troubleshooting/preview-errors)
