I have a app named Only-bundles which i submitted to shopify after deploying it to render but I when app loads in store it shows Blocked script execution because allow scripts are not set but I have set all CRP headers and correct app bridge initialization because shopify is loading my app in sandbox and something related to iframe, the shopify partner dashboard config and my code config are exact match but still what to do
see my code
entry.server.jsx
import { RemixBrowser } from "@remix-run/react"
import { startTransition, StrictMode } from "react"
import { hydrateRoot } from "react-dom/client"
// Simple logging helper
const log = (message, data) => {
console.log(`[Shopify App] ${message}`, data || "")
}
// Error logging helper
const logError = (message, error) => {
console.error(`[Shopify App Error] ${message}`, error)
}
// Improve the App Bridge initialization logic
function initializeAppBridge() {
try {
if (typeof window === "undefined" || !window.shopify?.AppBridge) {
logError("App Bridge not available")
return null
}
// Get query parameters from URL
const searchParams = new URLSearchParams(window.location.search)
const shop = searchParams.get("shop")
const host = searchParams.get("host")
// Get API key from meta tag
const apiKeyMeta = document.querySelector('meta[name="shopify-api-key"]')
const apiKey = apiKeyMeta ? apiKeyMeta.getAttribute("content") : null
// Fallback to global variable
const globalApiKey = window.shopify?.apiKey
// Use the actual API key from your environment
const finalApiKey = apiKey || globalApiKey || "8d4e0e05f2b0106c384f977e8f218792"
log("App Bridge configuration parameters:", {
apiKey: finalApiKey ? "Set" : "Not set",
shop,
host,
})
if (!finalApiKey) {
logError("Missing API key for App Bridge initialization")
return null
}
if (!shop || !host) {
logError("Missing shop or host for App Bridge initialization")
return null
}
const AppBridge = window.shopify.AppBridge
// Configure App Bridge
const appBridgeConfig = {
apiKey: finalApiKey,
host: host,
forceRedirect: false,
}
// Create App Bridge instance
const app = AppBridge.createApp(appBridgeConfig)
// Store the instance globally for easy access
window.app = app
log("App Bridge initialized successfully")
return app
} catch (error) {
logError("Error during App Bridge initialization:", error)
return null
}
}
// Hydrate the app
function hydrateApp() {
log("Starting hydration")
try {
startTransition(() => {
hydrateRoot(
document,
<StrictMode>
<RemixBrowser />
</StrictMode>,
)
log("App hydrated successfully")
})
} catch (error) {
logError("Error during hydration:", error)
}
}
// Main initialization function
function initialize() {
log("Initializing app")
// First hydrate the app
hydrateApp()
// Then initialize App Bridge if it's loaded
if (window.shopify?.AppBridge) {
initializeAppBridge()
} else {
log("App Bridge not available")
}
}
// Wait for DOM to be ready
if (typeof document !== "undefined") {
if (document.readyState === "loading") {
log("Document still loading, waiting for DOMContentLoaded")
document.addEventListener("DOMContentLoaded", initialize)
} else {
log("Document already loaded, initializing immediately")
initialize()
}
}
entry.client.jsx
import { RemixBrowser } from "@remix-run/react"
import { startTransition, StrictMode } from "react"
import { hydrateRoot } from "react-dom/client"
// Simple logging helper
const log = (message, data) => {
console.log(`[Shopify App] ${message}`, data || "")
}
// Error logging helper
const logError = (message, error) => {
console.error(`[Shopify App Error] ${message}`, error)
}
// Improve the App Bridge initialization logic
function initializeAppBridge() {
try {
if (typeof window === "undefined" || !window.shopify?.AppBridge) {
logError("App Bridge not available")
return null
}
// Get query parameters from URL
const searchParams = new URLSearchParams(window.location.search)
const shop = searchParams.get("shop")
const host = searchParams.get("host")
// Get API key from meta tag
const apiKeyMeta = document.querySelector('meta[name="shopify-api-key"]')
const apiKey = apiKeyMeta ? apiKeyMeta.getAttribute("content") : null
// Fallback to global variable
const globalApiKey = window.shopify?.apiKey
// Use the actual API key from your environment
const finalApiKey = apiKey || globalApiKey || "api_key"
log("App Bridge configuration parameters:", {
apiKey: finalApiKey ? "Set" : "Not set",
shop,
host,
})
if (!finalApiKey) {
logError("Missing API key for App Bridge initialization")
return null
}
if (!shop || !host) {
logError("Missing shop or host for App Bridge initialization")
return null
}
const AppBridge = window.shopify.AppBridge
// Configure App Bridge
const appBridgeConfig = {
apiKey: finalApiKey,
host: host,
forceRedirect: false,
}
// Create App Bridge instance
const app = AppBridge.createApp(appBridgeConfig)
// Store the instance globally for easy access
window.app = app
log("App Bridge initialized successfully")
return app
} catch (error) {
logError("Error during App Bridge initialization:", error)
return null
}
}
// Hydrate the app
function hydrateApp() {
log("Starting hydration")
try {
startTransition(() => {
hydrateRoot(
document,
<StrictMode>
<RemixBrowser />
</StrictMode>,
)
log("App hydrated successfully")
})
} catch (error) {
logError("Error during hydration:", error)
}
}
// Main initialization function
function initialize() {
log("Initializing app")
// First hydrate the app
hydrateApp()
// Then initialize App Bridge if it's loaded
if (window.shopify?.AppBridge) {
initializeAppBridge()
} else {
log("App Bridge not available")
}
}
// Wait for DOM to be ready
if (typeof document !== "undefined") {
if (document.readyState === "loading") {
log("Document still loading, waiting for DOMContentLoaded")
document.addEventListener("DOMContentLoaded", initialize)
} else {
log("Document already loaded, initializing immediately")
initialize()
}
}
root.jsx
import { Links, Meta, Outlet, Scripts, ScrollRestoration, useLoaderData, useRouteError } from "@remix-run/react"
import { json } from "@remix-run/node"
import { AppProvider } from "@shopify/shopify-app-remix/react"
import { authenticate } from "./shopify.server"
import translations from "@shopify/polaris/locales/en.json"
import "@shopify/polaris/build/esm/styles.css"
import "./styles.css"
import { generateCSPHeaders } from "./utils/csp-headers"
export const loader = async ({ request }) => {
try {
// Get the shop from the URL if available
const url = new URL(request.url)
const shop = url.searchParams.get("shop")
const embedded = url.searchParams.get("embedded") === "1"
const host = url.searchParams.get("host")
// Try to authenticate the request
let sessionShop = null
try {
const { session } = await authenticate.admin(request)
sessionShop = session?.shop
} catch (authError) {
console.log("Authentication error (expected during OAuth flow):", authError.message)
}
// Use the shop from the session or URL
const shopDomain = sessionShop || shop
// Get API key from environment variable
const apiKey = process.env.SHOPIFY_API_KEY
// Log important values for debugging
console.log("Root loader values:", {
apiKey: apiKey ? "Set" : "Not set",
shopDomain,
embedded,
host,
url: request.url,
})
// Get CSP headers
const headers = generateCSPHeaders()
return json(
{
apiKey,
shop: shopDomain,
host,
embedded,
},
{ headers },
)
} catch (error) {
console.error("Loader error:", error)
// Return a basic response with CSP headers
return json(
{
apiKey: process.env.SHOPIFY_API_KEY,
shop: null,
host: null,
embedded: false,
},
{
headers: generateCSPHeaders(),
},
)
}
}
export default function App() {
const { apiKey, shop, host, embedded } = useLoaderData()
// Log important values for debugging
console.log("App component values:", {
apiKey: apiKey ? "Set" : "Not set",
shop,
host,
embedded,
})
if (!apiKey) {
return (
<div className="p-4">
<div className="bg-critical/10 p-4 rounded-md">
<h1 className="text-lg font-medium text-critical">Configuration Error</h1>
<p className="mt-2 text-sm text-critical">Missing API Key. Please check your environment variables.</p>
</div>
</div>
)
}
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="shopify-api-key" content={apiKey} />
{/* Remove defer attribute to ensure App Bridge loads before other scripts */}
<script src="https://cdn.shopify.com/shopifycloud/app-bridge.js"></script>
<Meta />
<Links />
</head>
<body>
<AppProvider
isEmbeddedApp
apiKey={apiKey}
host={host || ""}
shop={shop || ""}
forceRedirect={!embedded && !!shop}
i18n={translations}
>
<Outlet context={{ shop, host }} />
</AppProvider>
<ScrollRestoration />
<script
dangerouslySetInnerHTML={{
__html: `window.shopify = {
apiKey: "${apiKey}",
shop: "${shop || ""}",
host: "${host || ""}",
}`,
}}
/>
<Scripts />
</body>
</html>
)
}
export function ErrorBoundary() {
const error = useRouteError()
console.error("Root error:", error)
let errorMessage = "An unexpected error occurred"
let errorTitle = "Application Error"
let errorDetails = null
if (error instanceof Error) {
errorMessage = error.message
errorDetails = error.stack
}
if (error?.status === 401) {
errorTitle = "Authentication Error"
errorMessage = "Please refresh the page or try logging in again."
}
return (
<html lang="en">
<head>
<title>{errorTitle}</title>
<Meta />
<Links />
</head>
<body>
<div className="p-4">
<div className="bg-destructive/10 p-4 rounded-md">
<h1 className="text-lg font-medium text-destructive">{errorTitle}</h1>
<p className="mt-2 text-sm text-destructive">{errorMessage}</p>
{process.env.NODE_ENV === "development" && errorDetails && (
<pre className="mt-4 text-xs text-destructive/75 whitespace-pre-wrap">{errorDetails}</pre>
)}
</div>
</div>
<Scripts />
</body>
</html>
)
}