> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/preactjs/preact/llms.txt
> Use this file to discover all available pages before exploring further.

# cloneElement

> Clone and modify a Preact virtual node

Clones a virtual node (VNode) and optionally merges new props or replaces its children.

## Signature

```typescript theme={null}
function cloneElement(
  vnode: VNode<any>,
  props?: any,
  ...children: ComponentChildren[]
): VNode<any>

function cloneElement<P>(
  vnode: VNode<P>,
  props?: any,
  ...children: ComponentChildren[]
): VNode<P>
```

## Parameters

<ParamField path="vnode" type="VNode" required>
  The virtual node to clone. Must be a valid VNode created with `h()` or `createElement()`.
</ParamField>

<ParamField path="props" type="object">
  Additional props to merge with the original VNode's props. Special props like `key` and `ref` are handled separately.
</ParamField>

<ParamField path="children" type="ComponentChildren">
  New children to replace the original VNode's children. If provided, these replace (not merge with) the original children.
</ParamField>

## Return Value

Returns a new `VNode` with:

* Same `type` as the original
* Merged props (original + new)
* New children if provided, otherwise original children
* New `key` if provided, otherwise original key
* New `ref` if provided, otherwise original ref

## Description

`cloneElement` creates a shallow copy of a virtual node, allowing you to modify its props and children. This is useful for:

* Wrapping or modifying elements passed as children
* Adding props to elements dynamically
* Implementing higher-order components
* Extending component functionality

The function preserves the original VNode's type while merging new props and optionally replacing children.

## Implementation

The function is implemented in `src/clone-element.js:14`:

```javascript theme={null}
export function cloneElement(vnode, props, children) {
  let normalizedProps = assign({}, vnode.props),
    key,
    ref,
    i;

  for (i in props) {
    if (i == 'key') key = props[i];
    else if (i == 'ref' && typeof vnode.type != 'function') ref = props[i];
    else normalizedProps[i] = props[i];
  }

  if (arguments.length > 2) {
    normalizedProps.children =
      arguments.length > 3 ? slice.call(arguments, 2) : children;
  }

  return createVNode(
    vnode.type,
    normalizedProps,
    key || vnode.key,
    ref || vnode.ref,
    NULL
  );
}
```

## Usage Examples

### Basic Cloning

```jsx theme={null}
import { h, cloneElement } from 'preact';

const original = <div className="original">Hello</div>;

const cloned = cloneElement(original, {
  className: 'cloned',
  id: 'my-div'
});

// cloned:
// <div className="cloned" id="my-div">Hello</div>
```

### Adding Props to Children

```jsx theme={null}
import { cloneElement } from 'preact';

function ParentComponent({ children }) {
  // Add onClick handler to child
  return cloneElement(children, {
    onClick: () => console.log('Clicked!')
  });
}

// Usage:
<ParentComponent>
  <button>Click me</button>
</ParentComponent>

// Renders:
// <button onClick={...}>Click me</button>
```

### Replacing Children

```jsx theme={null}
import { h, cloneElement } from 'preact';

const original = <div>Original children</div>;

const withNewChildren = cloneElement(
  original,
  null,
  'New children'
);

// withNewChildren:
// <div>New children</div>
```

### Merging Props

```jsx theme={null}
import { h, cloneElement } from 'preact';

function withExtraProps(element) {
  return cloneElement(element, {
    className: `${element.props.className || ''} extra`,
    'data-enhanced': true
  });
}

const original = <div className="base">Content</div>;
const enhanced = withExtraProps(original);

// enhanced:
// <div className="base extra" data-enhanced={true}>Content</div>
```

### Extending Component Props

```jsx theme={null}
import { cloneElement } from 'preact';

function Button({ children, onClick }) {
  return <button onClick={onClick}>{children}</button>;
}

function TrackedButton({ children }) {
  const handleClick = (e) => {
    console.log('Button clicked');
    // Call original onClick if it exists
    children.props.onClick?.(e);
  };
  
  return cloneElement(children, { onClick: handleClick });
}

// Usage:
<TrackedButton>
  <Button onClick={() => alert('Original')}>Click me</Button>
</TrackedButton>
```

### Working with React.Children Pattern

```jsx theme={null}
import { cloneElement, toChildArray } from 'preact';

function List({ children }) {
  return (
    <ul>
      {toChildArray(children).map((child, index) =>
        cloneElement(child, {
          key: index,
          className: `list-item ${child.props.className || ''}`,
          'data-index': index
        })
      )}
    </ul>
  );
}

// Usage:
<List>
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
</List>

// Renders:
// <ul>
//   <li key="0" className="list-item" data-index="0">Item 1</li>
//   <li key="1" className="list-item" data-index="1">Item 2</li>
//   <li key="2" className="list-item" data-index="2">Item 3</li>
// </ul>
```

### Adding Event Handlers

```jsx theme={null}
import { cloneElement } from 'preact';

function ClickLogger({ children }) {
  const logClick = (e) => {
    console.log('Clicked:', e.target);
    // Call original handler if exists
    children.props.onClick?.(e);
  };
  
  return cloneElement(children, { onClick: logClick });
}

// Usage:
<ClickLogger>
  <button onClick={() => alert('Hello')}>Click me</button>
</ClickLogger>
```

### Updating Keys

```jsx theme={null}
import { cloneElement } from 'preact';

function KeyedList({ items, renderItem }) {
  return items.map(item => {
    const element = renderItem(item);
    return cloneElement(element, { key: item.id });
  });
}

// Usage:
const items = [
  { id: 1, name: 'Apple' },
  { id: 2, name: 'Banana' }
];

<KeyedList
  items={items}
  renderItem={item => <div>{item.name}</div>}
/>
```

### Modifying Refs

```jsx theme={null}
import { createRef, cloneElement } from 'preact';

function RefWrapper({ children }) {
  const myRef = createRef();
  
  const handleMount = () => {
    console.log('Element:', myRef.current);
  };
  
  useEffect(handleMount, []);
  
  // Replace child's ref with our own
  return cloneElement(children, { ref: myRef });
}

// Usage:
<RefWrapper>
  <input type="text" />
</RefWrapper>
```

### Higher-Order Component Pattern

```jsx theme={null}
import { cloneElement } from 'preact';
import { useState } from 'preact/hooks';

function withToggle(Component) {
  return function ToggleWrapper(props) {
    const [isOpen, setIsOpen] = useState(false);
    
    return cloneElement(Component, {
      ...props,
      isOpen,
      toggle: () => setIsOpen(!isOpen)
    });
  };
}

function Dropdown({ isOpen, toggle, items }) {
  return (
    <div>
      <button onClick={toggle}>Toggle</button>
      {isOpen && <ul>{items.map(item => <li>{item}</li>)}</ul>}
    </div>
  );
}

const EnhancedDropdown = withToggle(<Dropdown items={['A', 'B', 'C']} />);
```

### Conditionally Cloning

```jsx theme={null}
import { cloneElement } from 'preact';

function ConditionalWrapper({ condition, wrapper, children }) {
  if (condition) {
    return cloneElement(wrapper, null, children);
  }
  return children;
}

// Usage:
<ConditionalWrapper
  condition={isHighlighted}
  wrapper={<div className="highlight" />}
>
  <p>This might be highlighted</p>
</ConditionalWrapper>
```

### With TypeScript

```tsx theme={null}
import { cloneElement, VNode } from 'preact';

interface ButtonProps {
  onClick: () => void;
  disabled?: boolean;
}

function enhanceButton(button: VNode<ButtonProps>): VNode<ButtonProps> {
  return cloneElement(button, {
    className: 'enhanced-button',
    onClick: () => {
      console.log('Enhanced click');
      button.props.onClick?.();
    }
  });
}
```

## Important Behaviors

### Props Merging

New props are merged with original props, not replaced:

```jsx theme={null}
const original = <div className="foo" id="bar">Text</div>;
const cloned = cloneElement(original, { className: 'baz' });

// cloned props: { className: 'baz', id: 'bar' }
// className is overwritten, id is preserved
```

### Children Replacement

Children are replaced, not merged:

```jsx theme={null}
const original = <div>Original</div>;
const cloned = cloneElement(original, null, 'New');

// cloned: <div>New</div>
// "Original" is completely replaced
```

### Key and Ref Handling

`key` and `ref` are special props:

```jsx theme={null}
const original = <div key="old" ref={oldRef}>Text</div>;
const cloned = cloneElement(original, { key: 'new', ref: newRef });

// cloned has key="new" and ref={newRef}
// For function components, ref is passed as a prop
```

### Shallow Clone

`cloneElement` performs a shallow clone:

```jsx theme={null}
const original = <div data={{ nested: 'value' }}>Text</div>;
const cloned = cloneElement(original, {});

// cloned.props.data === original.props.data (same reference)
// Modifying cloned.props.data.nested affects original
```

## Common Pitfalls

### Not Preserving Original Handlers

```jsx theme={null}
// ❌ Overwrites original onClick
cloneElement(child, {
  onClick: () => console.log('New handler')
});

// ✅ Preserves original onClick
cloneElement(child, {
  onClick: (e) => {
    child.props.onClick?.(e);
    console.log('Additional handler');
  }
});
```

### Cloning Non-VNodes

```jsx theme={null}
// ❌ Doesn't work with strings, numbers, or null
cloneElement('text', {}); // Error
cloneElement(null, {}); // Error

// ✅ Only clone VNodes
if (typeof child === 'object' && child.type) {
  cloneElement(child, {});
}
```

## Performance Considerations

* `cloneElement` creates a new VNode object
* Original VNode is not modified (immutable)
* Minimal overhead - only copies necessary properties
* Use sparingly in performance-critical paths

## Related APIs

* [`h`](/api/h) - Create virtual nodes
* [`createElement`](/api/create-element) - React-compatible node creation
* `toChildArray` - Convert children to array for mapping
* `isValidElement` - Check if value is a VNode
