Intro about the series
Since this is the first article in the series I feel few words about the motivation for why I'm building this "coding toolshed"
For most of my coding career, I worked for big startups but recently I transitioned to be an Indi developer. Since I'm the CEO, CTO, COO, CFO and many other acronyms of whatever little startup I'm working at any month, I have full control and the freedom is intoxicating. However, working my way through two products, I found that decision fatigue is a real thing. When you spend a month in development and you are in no way near launch, it can be very frustrating, especially when you spend a lot of time perfecting a specific design for a form that you thought would only take a few hours, but ended up taking days.
So similar to the core ideas in things like Ruby on Rails and TailwindCss, I came to embrace Constraints and sensible defaults. Essentially I'm aiming to be in a position when I'm working on my startup I spend most of my time and brain power on business logic and for everything else, just plug & play.
To Clarify I never really stated any project from npm init
, actually for most of my recent projects has been built using the T3 stack. However, that is a good starting point but it's missing many things that I need often and I prefer to use different tools than the one in the default T3 stack. Hence my "coding toolshed"
So going forward I'm trying to spend a couple of days each week exploring and maintaining my coding toolshed. This series is a documentation of that.
Uploading Images
The last product I built was meant to allow realtors to have their sites. I don't remember exactly how long it took to code the logic and UI for uploading properties images, but between fighting with the Cloudinary file uploader, experimenting with uploading images from the server and designing a UI similar to Airbnb, I'm pretty sure I spent close around a week, (I'm in pain just remembering it 🥲).
So this week I explored different ways to develop a plug & play upload solution to use for all my projects. I almost deployed my very own hosting and CDN service on AWS until I came across UploadCare and after a few experimentations, they seemed like a very good fit for me.
Plug & Play Upload powered by UploadCare
First things first, you can find a demo of the final components here and the code here.
Using UploadCare's Uploader is a straightforward process, albeit with two particular exceptions that require special attention. Let's go over these elements and how I managed to navigate through them.
UploadCare uses Web Components
So if you don't know what are Web Component they are a standard way to use "Components" natively in the browser. They're part of the browser and so, they can extend HTML itself. As a rule of thumb, but more importantly, is the fact that they play well with many frameworks. Also, a Web Component's name must include a hyphen ("-"). Here's an example:
<lr-data-output ref={dataOutputRef} use-event hidden class={uniqueClass} onEvent={handleUploaderEvent}></lr-data-output>
As I mentioned already, one benefit of Web Components is that they can be readily used in a React environment. However, there's a trade-off: they don't offer typing support, which means no autocompletion. To work around this, I created a React component wrapper that provides strong typing for all consumers:
interface UploaderProps {
configsOverrides?: Partial<UploadCareConfig>;
setFiles?: (files: UploadCareFile[]) => void;
}
<Uploader configsOverrides={{ imgOnly: 1, multiple: 0 }} setFiles={(files) => setAvatar(files[0] || null)} />
UploadCare uses CSS variables to receive configs
Interestingly, UploadCare relies on CSS variables for configurations. This approach was new to me and I guess it was chosen due to the uploader being a Web Component, which would make it easier to provide support across different frameworks.
Configurations are essential when using an uploader. You'll likely want to tweak various settings to get things working just as you need them, especially when it comes to locale. To gain programmatic control and above all avoid dealing with passing configs in CSS variables directly, I extracted and stored all these configurations. The most challenging was local configurations due to their variety. However, I managed to create an Arabic locale configuration with the help of ChatGpt, storing it in my coding toolshed for easy modifications.
Using UploadCare Images in Next.js
Luckily UploadCare makes it very easy to serve highly optimized images to your users. All you have to do is use their Loader with the Next.js Image component and pass the optimizations, need. You can see my configs below that I think work for most cases.
const UploadCareImage: React.FC<UploadCareImageProps> = ({ src, alt, operations = "", ...props }) => {
const finalSrc = `${src as string}${operations}-/preview/-/quality/smart/-/format/auto/`;
return <Image src={finalSrc} loader={uploadcareLoader} alt={alt || ""} {...props} />;
};
Conclusion
Finally, after I finished the uploader I want to add some common let's call them blocks that uses the uploader. the next phase of our coding journey beckons. So I createdan Avatar and Gallery uploaders. Hopefully, these blocks will allow me to plug & play for future projects. You can see the code for work in progress tool shed here.