How I Avoid NextJS’s Image Component and Host Larger Files Elsewhere to Save Costs
If you want to keep your hosting costs low and not be forced to take the steep path from “Hobby” to “Pro”, avoid NextJS’s ‘Image’ component. Only 1,000 images are included in the free “Hobby” plan and even with a “Pro” plan it costs after 5,000 images; $5 per 1,000 images. Of course, you also avoid the “data transfer” limitations which you can hit fast with an embedded video and traffic.
I host all larger images and videos I use on SQLAI.ai (if you inspect the source code you can see most images and all videos use the subdomain files.sqlai.ai). The subdomain is associated with a free R2 bucket where I upload all large files. In SQLAI.ai, I keep it organized using a static configuration file (src/constants/static.ts
):
export const ROOT_URL = 'https://files.sqlai.ai';
export const screenshots = {
'generate-query-generated': `${ROOT_URL}/generate-query-generated.webp`,
'generate-query': `${ROOT_URL}/generate-query-screenshot.webp`,
'optimize-query': `${ROOT_URL}/optimize-query-screenshot.webp`,
'simplify-query': `${ROOT_URL}/simplify-query-screenshot.webp`,
'fix-query': `${ROOT_URL}/fix-query-screenshot.webp`,
'format-query': `${ROOT_URL}/format-query-screenshot.webp`,
'explain-sql': `${ROOT_URL}/explain-sql-screenshot.webp`,
};
export const videos = {
webm: {
'explain-sql-query': `${ROOT_URL}/explain-sql-query.webm`,
'fix-sql-query': `${ROOT_URL}/fix-sql-query.webm`,
'generate-sql-4k': `${ROOT_URL}/generate-sql-4k.webm`,
'optimize-sql-query': `${ROOT_URL}/optimize-sql-query.webm`,
'simplify-sql-query': `${ROOT_URL}/simplify-sql-query.webm`,
},
mp4: {
'explain-sql-query': `${ROOT_URL}/explain-sql-query.mp4`,
'fix-sql-query': `${ROOT_URL}/fix-sql-query.mp4`,
'generate-sql-4k': `${ROOT_URL}/generate-sql-4k.mp4`,
'optimize-sql-query': `${ROOT_URL}/optimize-sql-query.mp4`,
'simplify-sql-query': `${ROOT_URL}/simplify-sql-query.mp4`,
},
};
If I need a screenshot, I just import the screenshots and select the desired image through Typescript autocomplete:
<img src={screenshorts.["generate-query-generated"]} alt="Settings page" />
This renders as:
The same goes for video embeds:
<video controls preload="auto" controls autoPlay muted loop>
{/* Remember to put video/webm first so the browser will choose it */}
<source src={videos['webm']['generate-sql-4k']} type="video/webm" />
<source src={videos['mp4']['generate-sql-4k']} type="video/mp4" />
</video>
The advantages are that it’s free and relatively easy to maintain. When uploading a new file, I just add a new entry or if I overwrite a file then a cache bursting string to the existing entry, e.g. ${ROOT_URL}/generate-query-generated.webp?cache=burst
.