# react-perf/jsx-no-new-object-as-prop

> Disallow objects created during render as the value of a JSX prop

`{}` written inside a component makes a new object on every render. When you pass it as a prop, it's never equal to the value from the last render, so a child wrapped in `React.memo` renders again even though the contents are the same.

The rule reports object literals, `Object()`, `new Object()`, `Object.assign()` and `Object.create()`. An object at the top level of a module isn't reported, because it's created once.

{/* Generated by src/codegen/lint-rule-docs.ts. Do not edit this section. */}

## Configure

Off by default. To turn it on:

```toml bunfig.toml
[lint.rules]
"react-perf/jsx-no-new-object-as-prop" = "error" # or "warn"
```

Every option, set to its default:

```toml bunfig.toml
[lint.rules]
"react-perf/jsx-no-new-object-as-prop" = ["error", {
  nativeAllowList = [], # list of strings
}]
```

{/* End of the generated section. */}

## Examples

Reported:

{/* bad */}
```jsx
function Profile({ user }) {
  return <Avatar user={user} style={{ width: 32 }} />;
}
```

{/* bad */}
```jsx
function Profile({ defaults, user }) {
  return <Avatar user={Object.assign({}, defaults, user)} />;
}
```

The rule looks into `||`, `&&`, `??` and `? :`:

{/* bad */}
```jsx
function Profile({ user }) {
  return <Avatar user={user ?? {}} />;
}
```

A variable declared inside the component has the same problem as an object written in the prop, and so does a destructured prop's default. The report is on the declaration:

{/* bad {"count": 2} */}
```jsx
function Profile({ user = {} }) {
  const style = { width: 32 };
  return <Avatar user={user} style={style} />;
}
```

Not reported:

{/* good */}
```jsx
const avatarStyle = { width: 32 };
const guest = {};

function Profile({ user }) {
  return <Avatar user={user ?? guest} style={avatarStyle} />;
}
```

If the object depends on props or state, cache it with `useMemo`:

{/* good */}
```jsx
import { useMemo } from "react";

function Profile({ user, size }) {
  const style = useMemo(() => ({ width: size }), [size]);
  return <Avatar user={user} style={style} />;
}
```

## Options

| Option | Default | What it does |
| --- | --- | --- |
| `nativeAllowList` | `[]` | Props to skip on elements with a lowercase name, such as `<div>`. A list of prop names (case doesn't matter), or `"all"` for every prop. |

A DOM element doesn't compare its props to skip a render, so `style={{ .. }}` costs nothing there. With `{ "nativeAllowList": ["style"] }`, this is allowed:

{/* good {"options": [{"nativeAllowList": ["style"]}]} */}
```jsx
function Banner() {
  return <div style={{ padding: 8 }} />;
}
```

The element's other props are still checked, and so is `style` on a component:

{/* bad {"options": [{"nativeAllowList": ["style"]}], "count": 2} */}
```jsx
function Banner() {
  return (
    <map-view center={{ lat: 52, lng: 13 }}>
      <Badge style={{ padding: 8 }} />
    </map-view>
  );
}
```

## When not to use it

A new prop value only matters to a child that compares its props, for example with `React.memo`. The React Compiler memoizes these values for you.
