How use <Script /> with NextJS 14.X ?
Unanswered
Pacific herring posted this in #help-forum
Pacific herringOP
Okay I wanna make this code in HTML but working with NextJS 14 :
Ive made this :
But the script work only one time at page reload :
<div data-jw-widget
data-api-key="ABCdef12"
data-object-type="movie"
data-title="The Matrix"
data-year="1999"
data-theme="dark">
</div>
<script async src="https://widget.justwatch.com/justwatch_widget.js"></script> Ive made this :
<div data-jw-widget
data-api-key={process.env.NEXT_PUBLIC_JUSTWATCH_API_KEY}
data-object-type={type}
data-id={id}
data-id-type={idType}
data-max-offers={maxOffer}
data-theme={'dark'}
data-language={locale}
data-scale="0.8"
/>
<Script
async
src="https://widget.justwatch.com/justwatch_widget.js"
onReady={() => console.log(`JustWatch widget ready for ${id}`)}
/>But the script work only one time at page reload :
101 Replies
Pacific herringOP
any idea ?
Pacific herringOP
If I try this
I got this error :
useEffect(() => {
const srcUrl = `https://widget.justwatch.com/justwatch_widget.js`;
const s = document.createElement('script');
const addScript = src => {
s.setAttribute('src', src);
s.setAttribute('async', 'async');
s.setAttribute('id', `justwatch-widget-${id}`)
document.body.append(s);
s.remove()
};
addScript(srcUrl)
},[]);
return (
<div className="bg-red-500">
<div data-jw-widget
data-api-key={process.env.NEXT_PUBLIC_JUSTWATCH_API_KEY}
data-object-type={type}
data-id={id}
data-id-type={idType}
data-max-offers={maxOffer}
data-theme={'dark'}
data-language={locale}
data-scale="0.8"
/>
</div>
)I got this error :
There already is a globally defined variable with the name "JustWatch".
Aborting widget initialization.Pacific herringOP
Well the only way that I found is doing this :
But there is no cache (like if we use this widget for a movie id, if we come back to the same movie, the script gonna refresh for nothing)
useEffect(() => {
const srcUrl = `https://widget.justwatch.com/justwatch_widget.js`;
// @ts-ignore
if (window['JustWatch']) {
// @ts-ignore
delete window['JustWatch'];
}
const s = document.createElement('script');
const addScript = (src: string) => {
s.setAttribute('src', src);
s.setAttribute('async', 'async');
s.setAttribute('id', `justwatch-widget-${id}`)
document.body.append(s);
s.remove()
};
addScript(srcUrl);
return () => {
const script = document.getElementById(`justwatch-widget-${id}`);
if (script) {
script.remove();
}
}
},[id]);But there is no cache (like if we use this widget for a movie id, if we come back to the same movie, the script gonna refresh for nothing)
Pacific herringOP
Yes ahha
And when I got to another movie the script should be refresh or reexecuted for the new movie id
so for every movie
new script
but same movie, cached one should be loaded
if i was there already
Pacific herringOP
Yes, maybe with a state but seems complicated for nothing maybe ? Also in NextJS the script add a white background and Im not able to modifiy it I dont understand why, but maybe see this later
Like here u see a sandbox (send by JustWatch team), if I add a background to the div container of the widget, we have the right color background. But in react there is a white background idk why ahah
Asian black bear
taking a look, just got off a phone call
seems to be coming from the iframe?
curious
ure using a valid api key right?
@Asian black bear seems to be coming from the iframe?
Pacific herringOP
Yeah but that weird because the codepen version dont have iframe background
And in the css I dont see any background-color rules
@Asian black bear ure using a valid api key right?
Pacific herringOP
Yes Ive also try with the apikey from the codepen version
Asian black bear
ok, just wanted to make sure
Pacific herringOP
no rules anywhere 😅 (codepen version made by JustWatch team)
what if u check if the script
exists before
if it does you do an early return
🤔
also what is the
window['JustWatch']for?
@Asian black bear also what is the `window['JustWatch']`
Pacific herringOP
I use JustWatch to see if the script was already loaded because if I dont delete the global variable the script can be reloaded for another movie id... Without I got this :https://nextjs-forum.com/post/1235986120940982283#message-1236242445490983004
@Asian black bear what if u check if the script
Pacific herringOP
But the script have to replace the content of a div, so if I dont run the script if already exist, the div stay (so iframe isnt rendered)
Pacific herringOP
It seems that modification arent cached
Like here I try with only this (no
useEffect) :<div className="bg-red-500">
<div data-jw-widget
data-api-key={process.env.NEXT_PUBLIC_JUSTWATCH_API_KEY}
data-object-type={type}
data-id={id}
data-id-type={idType}
data-max-offers={maxOffer}
data-theme={'dark'}
data-language={locale}
data-scale="0.8"
/>
<div className="text-right">
<span className="text-sm text-muted-foreground">Powered by{' '}</span>
<Link
href={`https://www.justwatch.com/`}
// href="https://www.justwatch.com/us/movie/the-matrix"
target="_blank"
className="inline-flex items-center gap-1"
>
<Image
alt="JustWatch"
height={11}
width={70}
src="https://widget.justwatch.com/assets/JW_logo_color_10px.svg"
/>
</Link>
</div>
<Script
id={`justwatch-widget-${id}`}
async
src="https://widget.justwatch.com/justwatch_widget.js"
strategy="lazyOnload"
// onReady={() => console.log(`JustWatch widget ready for ${id}`)}
/>
</div>can we slow down a bit
how far have u gotten
if i understand, youre now able to load it for every movie
but the problem is that its not being cached?
if we could take a step back
@Asian black bear if i understand, youre now able to load it for every movie
Pacific herringOP
Yeah using a
useEffect im able to make it work for every movieAsian black bear
it'd help me understand
1. goal
2. current problem
3. what we want, the gap left to close
1. goal
2. current problem
3. what we want, the gap left to close
@Pacific herring Yeah using a `useEffect` im able to make it work for every movie
Asian black bear
can you show me that specific code again in a snippet
lets take it from there
@Asian black bear it'd help me understand
1. goal
2. current problem
3. what we want, the gap left to close
Pacific herringOP
1. Showing the widget for every movie
2. The iframe created for each movie is not cached, so each time the widget have to be show, there is a loading state for loading the widget
3. Cache the iframe if possible and change the background color ahah
2. The iframe created for each movie is not cached, so each time the widget have to be show, there is a loading state for loading the widget
3. Cache the iframe if possible and change the background color ahah
Asian black bear
lets focus on the caching
and deal with background later
can u show me the full code
currently
up to this point
sorry to reiterate
but i was confused, and its easier to solve it this way for me

Pacific herringOP
Wait I cant send u the code ahah (french error msg)
Asian black bear
"use client"
import { useLocale } from "next-intl"
import Image from "next/image"
import Link from "next/link"
import { usePathname } from "next/navigation"
import Script from "next/script"
import { useEffect, useState } from "react"
import { any } from "zod"
export async function JustWatchWidget({
id,
idType = 'tmdb',
title,
year,
theme = 'dark',
type = 'movie',
maxOffer = 4,
} : {
id: number,
idType?: string,
title?: string
year?: number,
theme?: string,
type?: string
maxOffer?: number
}) {
const locale = useLocale();
useEffect(() => {
const srcUrl = `https://widget.justwatch.com/justwatch_widget.js`;
// @ts-ignore
if (window['JustWatch']) {
// @ts-ignore
delete window['JustWatch'];
}
const s = document.createElement('script');
const addScript = (src: string) => {
s.setAttribute('src', src);
s.setAttribute('async', 'async');
s.setAttribute('id', `justwatch-widget-${id}`)
document.body.append(s);
s.remove()
};
addScript(srcUrl);
return () => {
const script = document.getElementById(`justwatch-widget-${id}`);
if (script) {
script.remove();
}
}
},[id]);
return (
<div className="bg-red-500">
<div data-jw-widget
data-api-key={process.env.NEXT_PUBLIC_JUSTWATCH_API_KEY}
data-object-type={type}
data-id={id}
data-id-type={idType}
data-max-offers={maxOffer}
data-theme={'dark'}
data-language={locale}
data-scale="0.8"
/>
<div className="text-right">
<span className="text-sm text-muted-foreground">Powered by{' '}</span>
<Link
href={`https://www.justwatch.com/`}
target="_blank"
className="inline-flex items-center gap-1"
>
<Image
alt="JustWatch"
height={11}
width={70}
src="https://widget.justwatch.com/assets/JW_logo_color_10px.svg"
/>
</Link>
</div>
</div>
)
}ok
caching it on the page is a bit tricky
but we can avoid making the request
by caching it in a Map
something lik
if (iframeCache[cacheKey]) {
// If it exists, append the cached iframe element to the document body
document.body.appendChild(iframeCache.get(cacheKey));
} else {
// If it doesn't exist, create a new iframe element and add it to the cache
const iframe = document.createElement('iframe');
iframe.src = srcUrl;
iframe.id = cacheKey;
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframeCache.set(cacheKey, iframe);
}🤔
that will avoid us making the request
if u wanna persist itt
u can use indexeddb
its accepts a maximum size bigger than local storage
localforage is a library that lets u work with indexed dbmakes it super easy to use it
ive used it myself, its a good library
yeah
i think that should be good
cache key would be
const cacheKey = justwatch-widget-${id}but in a string
Pacific herringOP
so iframeCache is a state ?
@Asian black bear cache key would be ` const cacheKey = justwatch-widget-${id}`
Pacific herringOP
Ive try this
But I have some error
@Pacific herring so iframeCache is a state ?
Asian black bear
something in indexed db
@Asian black bear something in indexed db
Pacific herringOP
But using useState is the same right ?
@Pacific herring But using useState is the same right ?
Asian black bear
not really
if someone closes the tab and comes back
useState isnt persisted
localStorage would also work
but idk how big those iframes are
if small in size, localhost is fine
@Asian black bear if someone closes the tab and comes back
Pacific herringOP
Oh yes but I dont really need to keep this data even when closing the tab
@Pacific herring Oh yes but I dont really need to keep this data even when closing the tab
Asian black bear
then map should be fine
useState
ure gonna lose it
when navigating back
bc of re renders
i would do map in memory
like in memory cache
Pacific herringOP
okay im gonna do this thx
And do u have an idea for the background color ? Because Ive try everything, like css rules, etc... but nothing work
@Pacific herring And do u have an idea for the background color ? Because Ive try everything, like css rules, etc... but nothing work
Asian black bear
Can peek into it later today
It's an interesting problem 😁😁