Title: "Objects are not valid as a React child" error in Admin Action UI Extension (Context.Provider)

Hi Shopify Community,

I’m encountering a persistent React rendering error in my Admin Action UI extension (admin.product-details.action.render) despite following the official docs and cleaning up my code. The error occurs in Shopify’s internal <Context.Provider> component:

text

ERROR: The above error occurred in the <Context.Provider> component:
ERROR: {
"name": "Error",
"message": "Objects are not valid as a React child (found: object with keys {type, props, key, ref, __k, __, __b, __e, __c, constructor, __v, __i, __u, __source, __self}). If you meant to render a collection of children, use an array instead."
}

My Setup:

  • Shopify CLI latest version

  • React flavor Admin Action extension (issue-tracker-action)

  • shopify app dev with app-preview working

  • Dependencies aligned (@shopify/ui-extensions-react/admin, React 18+)

Console logs show:

text

Issue_Tracker_Action data: [object with selected array]
Issue_Tracker_Action selected: [array of product IDs/objects]
Issue_Tracker_Action selectedFirst: [first product ID or object]

Files (complete code):

ActionExtension.jsx (src/ActionExtension.jsx):

jsx

[PASTE YOUR COMPLETE ActionExtension.jsx CODE HERE - the full file you shared]

utils.js (src/utils.js):

js

[PASTE YOUR COMPLETE utils.js CODE HERE - the full file you shared]

What I’ve tried:

  • Logging data, data.selected - they’re plain objects/arrays, not JSX

  • Stripping JSX to minimal <AdminAction><Box>Test</Box></AdminAction> - error persists

  • Verified no direct object rendering in my JSX

  • Aligned package versions, cleared cache, restarted dev server

  • Confirmed shopify.extension.toml points to correct module

The error suggests something in useApi(TARGET) or Shopify’s internal context is passing a React element object, but my code only renders strings/JSX primitives.

Questions:

  1. Is this a known issue with admin.product-details.action.render + React flavor?

  2. Should data.selected be handled differently for Admin Actions?

  3. Any required wrapper/index file I’m missing?

  4. Flavor mismatch (React vs Preact) despite using @shopify/ui-extensions-react/admin?

Would appreciate any insights or working examples! Happy to share package.json, shopify.extension.toml, or full repo.

Tags: #ui-extensions #admin-action #react #shopify-cli #debugging

Hi Jaytaraviya241,

This error is usually seen when trying to use html elements or custom React components in your extension. The only valid components for admin extensions are the ones listed here: https://shopify.dev/docs/api/admin-extensions/latest/polaris-web-components

Can you share your extension code with us so we can verify?

import { useCallback, useEffect, useState } from "react";

import {

reactExtension,

useApi,

TextField,

AdminAction,

Button,

TextArea,

Box,

} from "@shopify/ui-extensions-react/admin";

import { getIssues, updateIssues } from "./utils";



function generateId(allIssues) {

return !allIssues?.length ? 0 : allIssues\[allIssues.length - 1\].id + 1;

}



function validateForm({ title, description }) {

return {

isValid: Boolean(title) && Boolean(description),

errors: {

title: !title,

description: !description,

    },

  };

}



const TARGET = "admin.product-details.action.render";



export default reactExtension(TARGET, () => <App />);



function App() {

const { close, data } = useApi(TARGET);



console.log('Issue_Tracker_Action data:', data);



const selected = data?.selected;

console.log('Issue_Tracker_Action selected:', selected);

const selectedFirst = Array.isArray(selected) ? selected\[0\] : selected;

console.log('Issue_Tracker_Action selectedFirst:', selectedFirst);



const selectedProductId =

typeof selectedFirst === 'string'

? selectedFirst

: selectedFirst?.id;



// ... rest of your state / hooks




const \[issue, setIssue\] = useState({ title: "", description: "" });

const \[allIssues, setAllIssues\] = useState(\[\]);

const \[formErrors, setFormErrors\] = useState(null);

const { title, description } = issue;



useEffect(() => {

if (!selectedProductId) return;



getIssues(selectedProductId).then((issues) =>

setAllIssues(issues || \[\]),

    );

// eslint-disable-next-line react-hooks/exhaustive-deps

  }, \[selectedProductId\]);



const onSubmit = useCallback(async () => {

const { isValid, errors } = validateForm(issue);

setFormErrors(errors);



if (!isValid || !selectedProductId) return;



await updateIssues(selectedProductId, \[

...allIssues,

      {

id: generateId(allIssues),

completed: false,

...issue,

      },

    \]);

close();

  }, \[issue, allIssues, close, selectedProductId\]);



// If for some reason nothing is selected, render a simple message instead of breaking

if (!selectedProductId) {

return (

<AdminAction title="Create an issue">

<Box paddingBlockStart="large">

          No product selected.

</Box>

</AdminAction>

    );

  }



return (

<AdminAction

title="Create an issue"

primaryAction={<Button onPress={onSubmit}>Create</Button>}

secondaryAction={<Button onPress={close}>Cancel</Button>}

\>

<TextField

value={title}

error={formErrors?.title ? "Please enter a title" : undefined}

onChange={(val) => setIssue((prev) => ({ ...prev, title: val }))}

label="Title"

maxLength={50}

/>

<Box paddingBlockStart="large">

<TextArea

value={description}

error={

formErrors?.description ? "Please enter a description" : undefined

}

onChange={(val) =>

setIssue((prev) => ({ ...prev, description: val }))

}

label="Description"

maxLength={300}

/>

</Box>

</AdminAction>

  );

}

here my actionextension.jsx

The problem is in this section. You should use a Text element to wrap “No product selected”.