Load external javascript with saved settings using Shopify app

Topic summary

A developer is building a Shopify app using Shopify CLI that loads an external JavaScript script with user-configurable settings.

Current Progress:

  • Successfully implemented a settings form that captures user input (enableChatbot toggle and chatbotId)
  • Created loader and action functions to fetch and save settings to a database
  • Settings are being stored and retrieved properly

Current Challenge:

  • Attempting to inject the external script into the merchant’s storefront using the saved settings
  • Working on creating a script tag via Shopify’s REST API in the app/api/create-script.js route
  • The code attempts to POST to Shopify’s script_tags endpoint with the chatbot configuration

Technical Approach:

  • Using authenticateAdmin callback for OAuth validation
  • Trying to create a script tag that loads from https://app.thinkstack.ai/bot/thinkstackloader.min.js with the chatbotId parameter
  • Code includes error handling and JSON response formatting

Note: The developer mentions being new to Shopify and uncertain about the implementation approach. Some code snippets appear corrupted or reversed in the original post.

Summarized with AI on November 9. AI used: claude-sonnet-4-5-20250929.

I want to create an app that loads external script. I am using Shopify CLI to create app and I managed to take input form user and save the input as setting.

export const loader = async ({ request }) => {
  // Fetch current settings from your database
  const settings = await fetchSettingsFromDB();
  return json(settings);
};

export const action = async ({ request }) => {
  const formData = await request.formData();
  const enableChatbot = formData.get("enableChatbot") === "on";
  const chatbotId = formData.get("chatbotId");

  // Save settings to your database
  await saveSettingsToDB({ enableChatbot, chatbotId });

  return { success: true };
};
export default function App() {
  const settings = useLoaderData();
  const actionData = useActionData();

  const labelStyle = {
    display: "block",
    width: "100px",
    height: "40px",
    lineHeight: "40px",
  };

  const flex = {
    display: "flex",
    alignItems: "center",
    gap: "10px",
  };

  return (
    
  );
}

app/routes/_index.jsx

After this I want to load the script on the website the app is installed for. Here is what I am true.

export const action = async ({ request }) => {
  const { session, chatbotId } = await request.json();
  const client = new Shopify.Clients.Rest(session.shop, session.accessToken);

  try {
    const response = await client.post({
      path: "script_tags",
      data: {
        script_tag: {
          event: "onload",
          src: `https://app.thinkstack.ai/bot/thinkstackai-loader.min.js?id=${chatbotId}`,
        },
      },
      type: "application/json",
    });
    return json(response);
  } catch (error) {
    return json({ error: error.message }, { status: 500 });
  }
};

app/api/create-script.js

export const loader = async ({ request }) => {
  const url = new URL(request.url);
  const query = Object.fromEntries(url.searchParams.entries());
  const session = await Shopify.Auth.validateAuthCallback(request, query);

  const settings = await fetchSettingsFromDB();
  if (settings.enableChatbot) {
    const response = await fetch(`${url.origin}/api/create-script-tag`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ session, chatbotId: settings.chatbotId }),
    });

    if (!response.ok) {
      throw new Error("Failed to create script tag");
    }
  }

  return redirect(`https://${session.shop}/admin/apps`);
};

app/auth/callback.js

NOTE: I am new to Shopify and I am not sure what I am doing wrong.