How to generate Next/Image placeholder=blur? from url dynamically?
Unanswered
Channel catfish posted this in #help-forum
Channel catfishOP
Has anyone figured out how to generate this dynamically using an external CDN? I know this works out of the box with Next.js static imports but I am using Imgix and there doesnt seem to be a solution. I've tried to use a function to convert it on demand but I get an error that the function.startsWith() does not exist?
<Image
src={`https://bhris.imgix.net/${project.url}/${section.filename}?auto=compress`}
unoptimized
alt="Image"
width="0"
height="0"
style={{
width: "100%",
height: "auto",
borderRadius: "0.5rem",
}}
blurDataURL={() =>
getBase64FromUrl(
`https://bhris.imgix.net/${project.url}/${section.filename}?auto=compress?w=8`
)
}
placeholder="blur"
/>88 Replies
Channel catfishOP
I've also tried to host it on Vercel with static imports but it is not working either.
{section.type === "image" && (
<Image
src={`/projects/${project.url}/${section.filename}`}
alt="Image"
width="0"
height="0"
style={{
width: "100%",
height: "auto",
borderRadius: "0.5rem",
}}
placeholder="blur"
/>
)}This is a static import and it does not auto generate the blur file when using template literals.
@Channel catfish {section.type === "image" && (
<Image
src={`/projects/${project.url}/${section.filename}`}
alt="Image"
width="0"
height="0"
style={{
width: "100%",
height: "auto",
borderRadius: "0.5rem",
}}
placeholder="blur"
/>
)}
blurDataURL can only be omitted when using placeholder="blur" if the src is a static image import:import image from './example.png'
<Image src={image} placeholder="blur" />if you load it remotely like in this example then you need to specify the property separately
the reason being that the static import automatically calculates the blur property for you, if you request the image from anywhere else you don't have this data
Channel catfishOP
I did a static import in the second example @Rafael Almeida
I used template literals to target the public folder directory but it doesnt seem to work
this is just a normal remote source, a static import is what I did in my snippet
Channel catfishOP
Sorry I think I misread. Is there a way to do imports dynamically?
I am mapping over json data to pull the images
I cant import directly into the page.tsx because that page is template for all my projects.
rather than a single page for one project
I think you would need an import map, are all images known when you are writing this component? if they are you could do it like this
import img1 from './images/img1.png'
import img2 from './images/img2.png'
const importMap = { img1, img2 }
// in your component
<Image src={importMap[key]} placeholder='blur' />Channel catfishOP
this kind of defeats the purpose of my page
I created one page.tsx under /projects/[project-name]
which will be the template for all my individual project pages.
Ideally, I want to pull in an image from the /public/ directly but not have to import every single one into the page first
maaybe you could try this:
I am not sure it works but worth a shot
async function Page() {
const img = await import(`./images/${foo}/${bar}.png`)
return <Image src={img} placeholder='blur' />
}I am not sure it works but worth a shot
Channel catfishOP
my imports will be filled
would that work in my map function?
{project.content.map((section, index) => (
<section key={index} id={index} className={styles.section}>
<motion.div
onViewportEnter={() => setActiveSection(index)}
initial={{ opacity: 0, scale: 0.75 }}
whileInView={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.5 }}
className={styles.section__content}
>
{section.type === "heading" && <h1>{section.body}</h1>}
{section.type === "paragraph" && <p>{section.body}</p>}
{section.type === "image" && (
<Image
src={`/projects/${project.url}/${section.filename}`}
alt="Image"
width="0"
height="0"
style={{
width: "100%",
height: "auto",
borderRadius: "0.5rem",
}}
placeholder="blur"
/>
)}
{section.type === "video" && (
<div className={styles.videoWrapper}>
<video
style={{ width: "100%", height: "100%" }}
controls
src={`https://stream.mux.com/${section.playbackID}/high.mp4`}
/>
</div>
)}
</motion.div>
</section>
))} `no, you need to await the import
Channel catfishOP
I have like 50+ images in my portfolio
you need to create the array of resolved imports before trying to render them in the jsx
would it allow me to write that once without defining a unique "img" name (ie. img1, img2, img3, ...)
the only other solution I can think of is to iterate over the entire /public/projects/ folder and import all of the images
recursively look into every folder and import any image found
which sounds over-engineered. idk if im thinking of this wrong
very simplified example (you might want to replace the array with an object since this relies on the order of
images): async function Page() {
const images = ['one.png', 'two.png']
const importedImages = await Promise.all(images.map(i => import(`./images/${i}`))
return <>{images.map((_, idx) => <Image src={importedImages[idx]} placeholder='blur' />}</>
}but you might want to test if the
await import works with a single image before trying to do it with all the images in a mapChannel catfishOP
ok I'm starting to see a solution forming
mainly in the promise.all
the format you did is definitely different than how i have my project set up but it could work with the map and import
would this affect performance of the web page at all?
I would have to filter it to only import images from the current projet url and not every single image in my public folder
a true static map would be definately faster but if the page is cached the performance wouldn't really matter
@Rafael Almeida a true static map would be definately faster but if the page is cached the performance wouldn't really matter
Channel catfishOP
with deprecation of getserversideprops how would I do this on serverside?
function extractImageFilenames(obj) {
const images = [];
if (obj.content) {
obj.content.forEach(item => {
if (item.type === "image") {
images.push(item.filename);
}
});
}
return images;
}this is how I am going to be generating my array of image file names
Channel catfishOP
@Rafael Almeida
got it to work
kind of different solution but it similar in theory
iterating over an array of all of the images in my project and importing them with a map
thanks man
oh nice, glad you got it working 

Channel catfishOP
we will see if this actually works in practice
@Rafael Almeida is there a way to do this logic serverside?
I'm currently doing it in a useEffect which might defeat the purpose of generating these quickly
ideally I want the images to be available immediately
im used to using getserversideprops but its been deprecated
are you using the app dir? you can do it directly in the body of component if it is a server component
Channel catfishOP
yes app dir
async function importAllImages(project) {
const imagesArray = extractImageFileNames(project);
const importedImages = [];
function extractImageFileNames(project) {
const images = [];
if (project.content) {
project.content.forEach((item) => {
if (item.type === "image") {
images.push(item.filename);
}
});
}
return images;
}
for (const image of imagesArray) {
const importedImage = await import(
`public/projects/${project.url}/${image}`
);
importedImages.push(importedImage.default);
}
return importedImages;
}
export default async function Home({ params }) {
const { project: projectUrl } = params;
const project = data.find((project) => project.url === projectUrl);
const [activeSection, setActiveSection] = useState(0);
const scrollRef = useRef();
const { scrollYProgress, scrollY } = useScroll({ container: scrollRef });
const [isOpen, setIsOpen] = useState(false);
// const scope = useMenuAnimation(isOpen);
const toggleModal = () => {
setIsOpen(!isOpen);
};
useEffect(() => {
importAllImages(project)
.then((images) => {
// Use the imported images here
console.log(images);
})
.catch((error) => {
// Handle any errors that occurred during the import
console.error(error);
});
}, []);`in brief this is what it looks like right now
see my latest snippet, I am doing it directly in the component since it can be async
but server components can't have state, so you will need to move all this
useState stuff to a separate component with 'use client'Channel catfishOP
how would you change my solution to be serverside?
move the importAllImages function into the Home?
yeah
export default async function Home({ params }) {
const { project: projectUrl } = params;
const project = data.find((project) => project.url === projectUrl);
const importedImages = await importAllImages(project)
// ...
}Channel catfishOP
Its stuck in an infinite loop right now
make sure the client component isn't using the
async keywordin this example both are async
the first snippet? yeah this is a server component file, it doesn't have state
Channel catfishOP
im not going to lie im stuck
async function importAllImages(project) {
const imagesArray = extractImageFileNames(project);
const importedImages = [];
function extractImageFileNames(project) {
const images = [];
if (project.content) {
project.content.forEach((item) => {
if (item.type === "image") {
images.push(item.filename);
}
});
}
return images;
}
for (const image of imagesArray) {
const importedImage = await import(
`public/projects/${project.url}/${image}`
);
importedImages.push(importedImage.default);
}
return importedImages;
}
export default function Home({ params }) {
const { project: projectUrl } = params;
const project = data.find((project) => project.url === projectUrl);
const [activeSection, setActiveSection] = useState(0);
const scrollRef = useRef();
const { scrollYProgress, scrollY } = useScroll({ container: scrollRef });
const [isOpen, setIsOpen] = useState(false);
const importedImages = importAllImages(project);
useEffect(() => {
console.log(importedImages);
}, [importedImages]);`the app dir introduced a few new concepts, the most important are the Server Components and Client Components, you can read more about it here: https://nextjs.org/docs/getting-started/react-essentials
in your example, you need to separate the interactive stuff (with
useState) and the server stuff into separate filesChannel catfishOP
ok I see. so if I split off the server stuff (importAllImages) into a new file
how would I use it in the client side code?
as I need the imported images for placeholder=blur
my brain is still wired to use getserversideprops and I'd just write it in a function outside of it but right now it doesnt seem you can do that.
the page would be like that
and the client component
// page.js
import YorClientPage from './client'
async function importAllImages(project) {
// ...
}
export default async function Home({ params }) {
const { project: projectUrl } = params;
const project = data.find((project) => project.url === projectUrl);
const importedImages = await importAllImages(project);
return <YourClientPage images={importedImages} />;
}and the client component
// client.js
'use client'
export default function ClientPage({ images }) {
// notice all the client interaction stuff like state is here
const [activeSection, setActiveSection] = useState(0);
const scrollRef = useRef();
const { scrollYProgress, scrollY } = useScroll({ container: scrollRef });
const [isOpen, setIsOpen] = useState(false);
// you can render the Images here using the data from the props
return ...
}Channel catfishOP
oh ok I see
so the client page becomes the returned value
and you leave all the client code in there
page.tsx is the serverside code
well you can do much more than that by rendering most components in the server component, but this is the simplest way to migrate from
getServerSidePropsChannel catfishOP
with /app/ directory what will the file structure look like?
you basically treat the
page.js file as the getServerSideProps and you pass the data to the client componentChannel catfishOP
whats the "official" way to do it
could I still do client side stuff in the same file?
the recommendation is to mix server and client components as needed, you only use client components for stuff that need client state and render all the rest in the server components
you will need to read the guide to understand better, is a different mental model that takes a bit to get used to