Skip to content

Commit 41c033d

Browse files
iHiDclaude
andauthored
Fix stylesheet loading to prevent hanging Turbo frames and potential TypeError (#8594)
Filter null/empty hrefs from link elements before loading stylesheets, add onerror handling so failed loads reject instead of hanging forever, and use Promise.allSettled to always resume rendering even if a stylesheet fails. Closes #8587 Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent fc292a9 commit 41c033d

1 file changed

Lines changed: 10 additions & 6 deletions

File tree

app/javascript/utils/react-bootloader.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,13 @@ const chunkErrorFallback: Sentry.FallbackRender = ({ error }) => {
166166

167167
// Asynchronously appends a stylesheet to the head and resolves
168168
// the promise when it's finished loading.
169-
let loadStylesheet = function (url) {
169+
const loadStylesheet = function (url: string): Promise<void> {
170170
return new Promise((resolve, reject) => {
171-
let link = document.createElement('link')
171+
const link = document.createElement('link')
172172
link.type = 'text/css'
173173
link.rel = 'stylesheet'
174-
link.onload = resolve
174+
link.onload = () => resolve()
175+
link.onerror = () => reject(new Error(`Failed to load stylesheet: ${url}`))
175176
link.href = url
176177

177178
document.getElementsByTagName('head')[0].appendChild(link)
@@ -188,7 +189,9 @@ function initEventListeners() {
188189

189190
const hrefs = Array.from(
190191
e.detail.newFrame.querySelectorAll('link[rel="stylesheet"]')
191-
).map((link) => (link as HTMLLinkElement).getAttribute('href'))
192+
)
193+
.map((link) => (link as HTMLLinkElement).getAttribute('href'))
194+
.filter((href): href is string => href != null && href.length > 0)
192195

193196
// If we have no stylesheets, just continue
194197
if (hrefs.length == 0) {
@@ -201,8 +204,9 @@ function initEventListeners() {
201204
// Load stylesheets in parallel asynchronously
202205
const promises = hrefs.map((href) => loadStylesheet(href))
203206

204-
// When they're all loaded, resume
205-
Promise.all(promises).then(() => e.detail.resume())
207+
// When they're all loaded (or if any fail), resume rendering.
208+
// Use allSettled so a failed stylesheet never blocks the Turbo frame.
209+
Promise.allSettled(promises).then(() => e.detail.resume())
206210
})
207211

208212
// This changes any extra things that need changing from the

0 commit comments

Comments
 (0)