I have recently started the 2023 redesign for
Antares Programming.
The old design is getting old, and the file organization is starting
to get hard to deal with. Plus, theming with both light and dark
color schemes are getting confusing, and since I plan to open a
YouTube channel for it next year, I want my new videos to appear on
the website’s frontpage, so the redesign will also have to deal with
that.
I have been working professionally as a software developer for
almost 4 years now, and not once have I ever touched ReactJS, Vue,
Angular, Svelte, and the like, which makes me wonder how I even made
it in the industry. To be fair, most of my work involved backend
projects even if I’m a frontend person myself. But all of my
projects in the past have not been enough to warrant me reaching for
JS frontend libraries.
For example, for Antares Programming, I have always used either
Liquid or Nunjucks for templating and UI, eliminating the need for
JS frameworks. And server-side rendered webpages have historically
been faster than ones rendered on the client side. I use JavaScript
for components, of course, but only sparsely to make certain
components interactive.
Of course, there is no doubt JS frameworks have their benefits. They
are popular after all, which must entail that they give something of
value. And there are definitely projects where choosing vanilla over
frontend frameworks does more harm than good, especially when a web
app is more on the interactive side.
For example, some web apps benefit from data binding. This is when a
UI component is tied to a data store or variable, and both of them
update each other. For example, a form input can be data-bound to an
object property, and whenever the user types in their data into the
form input, the object property updates accordingly. In a two-way
data binding, the form input’s contents are also updated to reflect
the object property’s value when it is changed by the database or
some other process. This is quite challenging to do in vanilla
JavaScript, and sometimes you might as well just build your own data
binding framework.
Reactivity also follows when you have data binding. You have to have
an effective way of propagating changes. This is especially
important when an object is data-bound to multiple UI components.
Frontend frameworks do this for you, but if you choose the vanilla
way, the closest you can do is to use events and observer objects
that watch your object and dispatch events to UI components whenever
their values change.
All benefits come with a cost. And for frontend frameworks, these
costs come in the form of performance, SEO, file size, debugging,
styling, and updates.
The most obvious one is performance. Frontend frameworks like
ReactJS, Vue, and Angular cater to as wide an audience as possible.
Therefore, they want to address as many developer pain points as
possible. But the reality is, in most projects, not all of these
pain points are present. This means that many projects take in
solutions to problems they don’t even have. For example, I have seen
learning developers who use ReactJS for their portfolios and blogs.
And I get it, they want to get employed and they want to show their
proficiency in this technology. But remember that ReactJS provides a
lot of functionality that these kinds of websites don’t need.
Portfolios and blogs are more content-based than interactive, so
things like the virtual DOM just add to the webpage’s bundle size
when the actual DOM just needs to be updated just once, namely, when
the page first loads.
Also, some websites are empty HTML documents until the framework
renders the entire page. Compare that to “old-school”
server-side-rendered webpages that sends a complete HTML document
that the browser can load and display as soon as it receives it.
Client-side rendering is also bad for SEO. Search engine crawlers
previously didn’t try to run JavaScript when they crawled websites,
which meant that if your website is heavily content-based, a lot of
your content wouldn’t appear on search results. This is okay, I
guess, if your website offers interactive utilities, like maybe
photo, video, or audio editing, then maybe your frontpage is the
only one that needs to be rendered server-side. But this is bad for
many e-commerce sites that need to have their products appear on
search results for people searching for things to buy. There are
solutions to this, of course, like server-side rendering, but then
again, it would just be like when we used to do web development with
PHP.
Styling is also a pain point in frameworks. CSS is a powerful
language. But with frameworks, its powerful systems for cascade,
inheritance, and specificity are nullified because everything are
styled on a component level. This is why things like CSS-in-JS are a
thing, because styling is hard to manage in the context of JS
frameworks. CSS is growing more and more powerful as the years go
by, and it’s a shame that we are doing everything in JavaScript.
Noam Rosenthal’s article, the two-part series
What Web Frameworks Solve
gave valuable insights as to how to build components without
JavaScript frameworks. I like this solution more because it makes me
use HTML, CSS, and JavaScript to their fullest potential.
Sending forms in just vanilla, for example, is much easier than some
people would think. Nowadays, we want forms to be sent in the
background, so we use the AJAX technique for that. By using the
document.forms property, you can access all of your
form’s values without tediously using
document.getElementById or
document.querySelector.
In this snippet, we gave the form a name
form-newsletter. We can then use that name to access
the form’s values via document.forms later on. Notice
that we also gave our input and
output elements a name attribute. Do not
confuse this with the id attribute, which we put in
there so we can attach the label elements to the form
inputs. The name attribute is what we’ll use to access
the values.
const frmNewsletter = document.forms['form-newsletter']
frmNewsletter.addEventListener('submit',e=>{
e.preventDefault()// so it doesn't refresh the page on submit.const fullname = frmNewsletter.elements['full-name'].value
const email = frmNewsletter.elements['email'].value
const output = frmNewsletter.elements['form-message']fetch('/api/v1/newsletter',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({'full-name': fullname,'email': email
})}).then(response=> response.json()).then(data=> output.message ='Form submitted successfully.').catch(err=>{
output.value ='Something went wrong. Check logs for details.'
console.error(err)})})
In this vanilla JS code, we have easily accessed the form inputs
using the document.forms property. We then used
fetch() to submit it to the server. Finally, we used
output to display the form submit’s result to the user.
What’s good about using output is that it is by default
aria-live in most browsers, which means that whenever
its contents are updated, assistive technologies like screen readers
will announce it to the user, thereby alerting them to the result of
the form submission.
And look at our form’s attributes, specifically the
action attribute. This means that by default, without
JavaScript, the form will submit to that URL in the
action attribute. That makes our form progressively
enhanced and we can now expect it to work on more browsers and even
when our JS code fails for some reason. If, for example, your API
only accepts JSON payloads since that is what we gave
fetch(), your form’s action attribute
could just point to a different endpoint that takes the form
submission, convert it into a JSON payload, and send it to the
actual API endpoint that will process the data.
One possible server architecture of a progressively-enhanced form.
HTML, CSS, and JavaScript are all powerful on their own. In reality,
frameworks are just there to make our jobs easier for us, and make
everything abstracted and less verbose. But considering the
trade-offs frameworks have, we must first look out and think if it’s
really worth it.
The Antares Programming redesign is almost complete. I’m estimating
around 80% of the site is complete at the time of writing. And right
now, my front-end JS is sitting at around 4.68kb minified but not
yet optimized, so every functionality across all pages are bundled
within one JS file. I expect that to be smaller if I separate them
across multiple bundles. And that’s a testament to the fact that
vanilla JS can be made more performant than frameworks. Why not give
it a try in your next project? It can be a fun challenge and a
rewarding experience.
You on the Fediverse yet?
If you have a Fediverse account, you can also send me a shoutout by
commenting on this post:
I wrote a blog post about doing things in vanilla JavaScript.
I'm really not a fan of frameworks, and since I'm
currently working on a website redesign, I thought I'd
write my thoughts here.
@noam
thank you! No I haven't, thanks also for linking that here
😄
@teacherbuknoy
I've recently done a few vanilla projects and I definitely
agree, it's nice to just quickly throw something together
without hassle. Also, I'm embarrassed to say I've never seen an
output tag before, so it just goes to show HTML 5 can do a lot
more than we give it credit for
@mostlyfocusedmike
don't stress it, I can say that output is one of the more
obscure ones in HTML. I've also known about this for a
while, but I didn't know it was aria-live by default until I
read that post by Noam.
@iamdtms
not really. More like building a cart from bits and pieces I
already made from other projects because I don't need an
entire car, although I'd reach for one if that's what I
need.