Narrow down string consts
// I normally used the following declaration and let typescript interfere myString: string[]
const myString = ['apple', 'banana', 'orange']
// But if you go with this instead
const myString = ['apple', 'banana', 'orange'] as const
// Tyescript now types myString as readonly ['apple', 'banana', 'orange']
// If you now want to use this
const myFunction = (arg: typeof myString[number]) = {}
Typeguard
interface MyType {
foo: number
}
const isMyType = (arg: any): arg is MyType => {
return arg.foo !== undefined
}
/**
* Sample usage of the User Defined Type Guard
*/
function doStuff(arg: Foo | Bar) {
if (isMyType(arg)) {
console.log(arg.foo); // OK
console.log(arg.bar); // Error!
}
else {
console.log(arg.foo); // Error!
console.log(arg.bar); // OK
}
}
Let's start by loading all images within a folder:
const imagesDirectory = path.join(
process.cwd(),
`public/products/${product.id}`
)
// Wrap the readdir in a try-catch block, otherwise the build will fail because of a unhandled exception.
try {
const productImagePaths = await fs.readdir(imagesDirectory)
return {
props: {
images: productImagePaths.map((path, index) => ({
path: `/products/${product.id}/${path}`,
})),
},
}
} catch (error) {
console.warn(
`Image ${product.name} has no images under /public/product/[id]!`
)
return {
props: {
images: [],
}
}
Part Two: Generate a BlueDataURL
The Nextjs Image Component provides a prop called blurDataURL
to show a very blurry version of the image while the actual image is loading. To generate this URL I use Plaiceholder:
const imagesDirectory = path.join(
process.cwd(),
`public/products/${product.id}`
)
// Wrap the readdir in a try-catch block, otherwise the build will fail because of a unhandled exception.
try {
const productImagePaths = await fs.readdir(imagesDirectory)
/**
* Create blurDataURLs (base64) as image placeholders
*/
const blurDataURLs = await Promise.all(
productImagePaths.map(async src => {
const { base64 } = await getPlaiceholder(`/products/${product.id}/${src}`)
return base64
})
).then(values => values)
return {
props: {
images: productImagePaths.map((path, index) => ({
path: `/products/${product.id}/${path}`,
blurDataURL: blurDataURLs[index],
})),
},
}
} catch (error) {
console.warn(
`Image ${product.name} has no images under /public/product/[id]!`
)
return {
props: {
images: [],
},
}
}
Part Three: Make it look sexy
Now it's time to use the image source and blurDataURL with the Nextjs image component.
// Use whichever styling solution you want. But it is important to add 'transition' to the Image component
const AnimatedImage = styled(Image, {
transition: ".3s",
});
const ProductPage = ({ images }) => {
<ImageContainer>
{images.length ? (
<AnimatedImage
src={images[0].path}
layout="fill"
objectFit="cover"
alt={images[0].path}
placeholder="blur"
blurDataURL={images[0].blurDataURL}
/>
) : (
<Image
src={PlaceholderImage}
layout="fill"
objectFit="cover"
alt="placeholder"
/>
)}
</ImageContainer>
);
};
The important parts are:
placeholder
need to beblur
blurDataURL
needs to be the base64 string generated by plaiceholder
I use layout="fill"
, that's why the actual image size is not that important to me. If you need this data you can use sharp's metadata for this.