Skip to content

🏖️ fix: Sandpack ExternalResources for Static HTML Artifact Previews#12509

Merged
danny-avila merged 4 commits into
devfrom
claude/youthful-murdock
Apr 2, 2026
Merged

🏖️ fix: Sandpack ExternalResources for Static HTML Artifact Previews#12509
danny-avila merged 4 commits into
devfrom
claude/youthful-murdock

Conversation

@danny-avila

@danny-avila danny-avila commented Apr 1, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #12507

Sandpack's static template detects external resource type by matching the last file extension in the URL. The versioned Tailwind CDN path (https://cdn.tailwindcss.com/3.4.17) matches .17 instead of .js, causing injectExternalResources to throw "Unable to determine file type". Because the throw occurs inside the injection pipeline's try/catch, it also aborts all subsequent runtime injections (console hook, refresh listener).

  • Appended a #tailwind.js URL fragment hint so Sandpack's regex matches .js. Fragments are not sent to the server, so the CDN response is unchanged.
  • Extracted the options computation into a pure buildSandpackOptions helper in artifacts.ts for testability.
  • Added unit tests covering all template/config combinations for the new helper.

Change Type

  • Bug fix (non-breaking change which fixes an issue)

Testing

  1. Create an HTML artifact using a prompt that generates a text/html artifact with Tailwind classes (without an explicit Tailwind <script> tag in the content).
  2. Open the browser console and confirm no Runtime injection failed error appears.
  3. Verify the HTML artifact renders with Tailwind styles applied.
  4. Create a React artifact and confirm Tailwind styles still work.
  5. Run cd client && npx jest --no-coverage artifacts.test.ts — all tests pass.

Checklist

  • My code adheres to this project's style guidelines
  • I have performed a self-review of my own code
  • I have commented in any complex areas of my code
  • My changes do not introduce new warnings
  • I have written tests demonstrating that my changes are effective or that my feature works
  • Local unit tests pass with my changes

Copilot AI review requested due to automatic review settings April 1, 2026 22:27
@danny-avila danny-avila changed the base branch from main to dev April 1, 2026 22:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes Sandpack runtime injection errors for static HTML artifact previews by omitting externalResources for the static template, while preserving Tailwind loading for bundled templates and adjusting startup-config query caching behavior.

Changes:

  • Scope Sandpack externalResources to non-static templates to prevent “Unable to determine file type” injection failures in HTML previews.
  • Introduce an auth-aware startupConfig query key and update all startupConfig cache reads to use it.
  • Adjust query-enable timing during login and reduce repeated getConfigDefaults().interface calls by hoisting defaults.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
client/src/components/Artifacts/ArtifactPreview.tsx Builds Sandpack options per template, omitting externalResources for static previews
client/src/utils/artifacts.ts Provides shared Sandpack options/files (Tailwind CDN + shared /public/index.html) used by artifact previews
client/src/data-provider/Endpoints/queries.ts Adds startupConfigKey(isAuthenticated) and switches useGetStartupConfig to the new key
client/src/hooks/SSE/useEventHandlers.ts Updates startup-config cache reads and consolidates imports via ~/data-provider
client/src/hooks/Input/useQueryParams.ts Updates startup-config cache reads to the auth-aware query key
client/src/hooks/Input/useQueryParams.spec.ts Updates ~/data-provider mocking to preserve real exports like startupConfigKey
client/src/hooks/Conversations/useNavigateToConvo.tsx Updates startup-config cache reads to the auth-aware query key
client/src/hooks/Chat/useChatFunctions.ts Updates startup-config cache reads to the auth-aware query key
client/src/components/Nav/SettingsTabs/Data/ImportConversations.tsx Updates startup-config cache reads to the auth-aware query key and simplifies imports
client/src/hooks/Endpoint/useEndpoints.ts Uses a hoisted default interface config (getConfigDefaults().interface) as fallback
client/src/components/Chat/Menus/Endpoints/ModelSelector.tsx Uses a hoisted default interface config (getConfigDefaults().interface) as fallback
client/src/hooks/AuthContext.tsx Re-enables queries from AuthContext when auth becomes true
client/src/data-provider/Auth/mutations.ts Disables queries during login mutation and re-enables on error (success path now handled by AuthContext)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 42 to 51
const options: SandpackProviderProps['options'] = useMemo(() => {
const baseOptions = template === 'static' ? {} : sharedOptions;

if (!startupConfig) {
return sharedOptions;
return baseOptions;
}

return {
...sharedOptions,
...baseOptions,
bundlerURL: template === 'static' ? startupConfig.staticBundlerURL : startupConfig.bundlerURL,

Copilot AI Apr 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

baseOptions is set to {} for the static template, which drops all entries from sharedOptions (not just externalResources). This works today because sharedOptions only contains externalResources, but it’s easy to introduce a future regression if additional shared Sandpack options are added later and silently stop applying to static previews.

Consider instead preserving sharedOptions while explicitly omitting/emptying externalResources for static (e.g., set externalResources: []), so other shared option defaults remain consistent across templates.

Copilot uses AI. Check for mistakes.
Comment on lines 26 to 41
/**
* Auth-aware query key so unauthenticated (login page) and authenticated
* (chat page) configs are cached independently, preventing stale
* unauthenticated config from persisting after login.
*/
export const startupConfigKey = (isAuthenticated: boolean) =>
[QueryKeys.startupConfig, isAuthenticated] as const;

export const useGetStartupConfig = (
config?: UseQueryOptions<t.TStartupConfig>,
): QueryObserverResult<t.TStartupConfig> => {
const queriesEnabled = useRecoilValue<boolean>(store.queriesEnabled);
const user = useRecoilValue<t.TUser | undefined>(store.user);
return useQuery<t.TStartupConfig>(
[QueryKeys.startupConfig],
startupConfigKey(!!user),
() => dataService.getStartupConfig(),

Copilot AI Apr 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR introduces additional cross-cutting behavior changes beyond the Sandpack externalResources fix: an auth-aware startupConfigKey and updates to the startup config query key shape. This is a non-trivial change that can affect caching/invalidation across the app, but it isn’t mentioned in the PR description.

Please update the PR description to cover this change (or consider splitting it into a separate PR) so reviewers can evaluate the caching/auth implications explicitly.

Copilot uses AI. Check for mistakes.
The Tailwind CDN URL lacks a file extension, causing Sandpack's
static template to throw a runtime injection error. Static previews
already load Tailwind via a script tag in the shared index.html,
so externalResources is unnecessary for them.

Closes #12507
@danny-avila danny-avila force-pushed the claude/youthful-murdock branch from 4a6cade to 8c7e518 Compare April 1, 2026 22:34
- Surgically omit only externalResources for static templates
  instead of discarding all sharedOptions, preventing future
  regression if new template-agnostic options are added.
- Extract options logic into a pure, testable helper function.
- Add unit tests covering all template/config combinations.
@danny-avila

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3b7d3346d3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread client/src/utils/artifacts.ts Outdated
Sandpack's static template regex detects resource type from the URL's
last file extension. The versioned CDN path (/3.4.17) matched ".17"
instead of ".js", throwing "Unable to determine file type". Rather
than omitting externalResources for static templates (which would
remove the only Tailwind injection path for HTML artifacts that don't
embed their own script tag), append a #tailwind.js fragment hint so
the regex matches ".js". Fragments are not sent to the server, so
the CDN response is unchanged.
@danny-avila

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@danny-avila danny-avila changed the title 🏖️ fix: Omit Sandpack ExternalResources for Static HTML Artifact Previews 🏖️ fix: Sandpack ExternalResources for Static HTML Artifact Previews Apr 2, 2026
@danny-avila danny-avila merged commit 611a1ef into dev Apr 2, 2026
7 checks passed
@danny-avila danny-avila deleted the claude/youthful-murdock branch April 2, 2026 02:06
yidianyiko pushed a commit to yidianyiko/LibreChat that referenced this pull request Apr 13, 2026
…anny-avila#12509)

* fix: omit externalResources for static Sandpack previews

The Tailwind CDN URL lacks a file extension, causing Sandpack's
static template to throw a runtime injection error. Static previews
already load Tailwind via a script tag in the shared index.html,
so externalResources is unnecessary for them.

Closes danny-avila#12507

* refactor: extract buildSandpackOptions and add tests

- Surgically omit only externalResources for static templates
  instead of discarding all sharedOptions, preventing future
  regression if new template-agnostic options are added.
- Extract options logic into a pure, testable helper function.
- Add unit tests covering all template/config combinations.

* chore: fix import order and pin test assertions

* fix: use URL fragment hint instead of omitting externalResources

Sandpack's static template regex detects resource type from the URL's
last file extension. The versioned CDN path (/3.4.17) matched ".17"
instead of ".js", throwing "Unable to determine file type". Rather
than omitting externalResources for static templates (which would
remove the only Tailwind injection path for HTML artifacts that don't
embed their own script tag), append a #tailwind.js fragment hint so
the regex matches ".js". Fragments are not sent to the server, so
the CDN response is unchanged.
jcbartle pushed a commit to jcbartle/LibreChat that referenced this pull request May 11, 2026
…anny-avila#12509)

* fix: omit externalResources for static Sandpack previews

The Tailwind CDN URL lacks a file extension, causing Sandpack's
static template to throw a runtime injection error. Static previews
already load Tailwind via a script tag in the shared index.html,
so externalResources is unnecessary for them.

Closes danny-avila#12507

* refactor: extract buildSandpackOptions and add tests

- Surgically omit only externalResources for static templates
  instead of discarding all sharedOptions, preventing future
  regression if new template-agnostic options are added.
- Extract options logic into a pure, testable helper function.
- Add unit tests covering all template/config combinations.

* chore: fix import order and pin test assertions

* fix: use URL fragment hint instead of omitting externalResources

Sandpack's static template regex detects resource type from the URL's
last file extension. The versioned CDN path (/3.4.17) matched ".17"
instead of ".js", throwing "Unable to determine file type". Rather
than omitting externalResources for static templates (which would
remove the only Tailwind injection path for HTML artifacts that don't
embed their own script tag), append a #tailwind.js fragment hint so
the regex matches ".js". Fragments are not sent to the server, so
the CDN response is unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Artifacts: avoid Sandpack static preview error for Tailwind CDN externalResources

2 participants