Add Less

When you’re about to start a new website, what do you think first? Do you start with a library or framework you know, like React or Vue, or a meta-framework on top of that, like Next or Nuxt? Do you pull up a speedy build tool like Vite, or configure your webpack?

There’s a great tweet by Phil Hawksworth that I bookmarked a few years back and still love to this day:

Your websites start fast until you add too much to make them slow. Do you need any framework at all? Could you do what you want natively in the browser? Would doing it without a framework at all make your site lighter, or actually heavier in the long run as you create or optimize what others have already done?

I personally love the idea of shipping less code to ultimately ship more value to the browser. Understanding browser APIs and what comes “for free” could actually lead to less reinventing the wheel, and potentially more accessibility as you use the tools provided.

Instead of pulling in a library for every single task you want to do, try to look under the hood at what they are doing. For example, in a project I was maintaining, I noticed that we had a React component imported that was shipping an entire npm package for a small (less than 10-line) component with some CSS sprinkled on top (that we were overriding with our own design system). When we re-wrote that component from scratch, our bundle size was smaller, we were able to customize it more, and we didn’t have to work around someone else’s decisions.

Now, I’m not saying you shouldn’t use any libraries or frameworks or components out there. Open source exists for a reason! What I am saying is to be discerning about what you bring into your projects. Let the power of the browser work for you, and use less stuff!


Add Less originally published on CSS-Tricks

ISR vs DPR: Big Words, Quick Explanation

If you’ve been dabbling in the Jamstack/page rendering/Next.js world, chances are you’ve heard of the terms “Incremental Static Regeneration” (ISR) and “distributed persistent rendering” (DPR) floating around. And if you haven’t, you might be like, “Wow, these are long words that I’ll never understand.” That’s where you’re wrong! You’re about to understand them now.

What Are These Things?

These terms are strategies for incrementally building websites. Normally, when you deploy a website that isn’t server-side or client-side rendered, it has to be compiled and built for the browser to natively load it (so, for example, your JSX is transpiled to vanilla JavaScript, your SCSS compiled to vanilla CSS, your templates into HTML).

As your websites get large, you might start to run into having to have fairly long build times, because that’s a lot to compile. Generally, your websites might have pages in two categories:

  • Type A
    “Critical” pages (home page, about us, contact us)
  • Type B
    “Deferred” pages that might not be hit as often as the Type A pages (product catalog, certain documentation pages)

If you were to incrementally build this website, you could theoretically break up your build where your Type A pages are built upfront, and then Type B pages are built later!

Both ISR and DPR follow this approach but do it in slightly different ways.

What’s The Difference Between ISR And DPR?

For both approaches, Type A pages are built upfront right when you deploy. With Type B pages, they vary a little more. The key thing that is different between these two approaches is immutability. When I say immutability, I mean that once a page is added to a build, it doesn’t change, and every user hitting a URL during that deployment will always see the exact same data.

With ISR, your Type B pages are built at runtime when a user goes to the page. Each of these pages has an “expiration time” (or “revalidation time”) where it will re-build based on new content that comes in at that time (fetched in the background). It is based on a caching strategy called “stale while revalidate”, meaning a page can be “stale” with old information until it’s re-generated and the cache is updated. This approach does not guarantee immutability across builds. When I say that, I mean that you can’t necessarily go back to a previous deployment with full confidence that all the content is going to be as it was at that time you originally ran that page (this is explained in another blog post from Lee Robinson on the Next.js team).

There are pros and cons to this; the major pro meaning that you can have your data that populates the page regularly update without rebuilding the site, and the major con being that debugging will be very difficult without that immutable piece (because some users might see a stale page and others might see the correctly updated one).

With DPR, your Type B pages are also built at runtime when a user goes to the page. When the non-built pages are requested for the first time, they are built and cached at the edge so they don’t need to be built again. Once it’s a part of the build, it will not change until a redeploy. There are also pros and cons to this approach. The major pro is that this guarantees immutability. When you go to a URL, every user will always see the same exact information. The con is that if you want to have one of those pages updated, you’ll need to trigger a rebuild of the site.

How Can I Use These Approaches And Compare For Myself?

In an ideal world, these approaches are built into the frameworks you use and you won’t have to do much to use them. Right now though, you can test it out with Next.js! ISR is built-in by default to Next.js when you deploy it to a Node.js-driven platform like Vercel, and DPR is built in when you deploy to Netlify.

You can use DPR in other frameworks as well (Zach Leatherman has some demos of it with Eleventy where he defers building hundreds of pages), and the Next.js team said that ISR will be coming to other frameworks in the future (like Nuxt and SvelteKit). You can also leave a comment on how DPR is implemented (for any Jamstack platform!) in this RFC.

Hack the “Deploy to Netlify” Button Using Environment Variables to Make a Customizable Site Generator

If you’re anything like me, you like being lazy shortcuts. The “Deploy to Netlify” button allows me to take this lovely feature of my personality and be productive with it.

Deploy to Netlify

Clicking the button above lets me (or you!) instantly clone my Next.js starter project and automatically deploy it to Netlify. Wow! So easy! I’m so happy!

Now, as I was perusing the docs for the button the other night, as one does, I noticed that you can pre-fill environment variables to the sites you deploy with the button. Which got me thinking… what kind of sites could I customize with that?

Ah, the famed “link in bio” you see all over social media when folks want you to see all of their relevant links in life. You can sign up for the various services that’ll make one of these sites for you, but what if you could make one yourself without having to sign up for yet another service?

But, we also are lazy and like shortcuts. Sounds like we can solve all of these problems with the “Deploy to Netlify” (DTN) button, and environment variables.

How would we build something like this?

In order to make our DTN button work, we need to make two projects that work together:

  • A template project (This is the repo that will be cloned and customized based on the environment variables passed in.)
  • A generator project (This is the project that will create the environment variables that should be passed to the button.)

I decided to be a little spicy with my examples, and so I made both projects with Vite, but the template project uses React and the generator project uses Vue.

I’ll do a high-level overview of how I built these two projects, and if you’d like to just see all the code, you can skip to the end of this post to see the final repositories!

The Template project

To start my template project, I’ll pull in Vite and React.

npm init @vitejs/app

After running this command, you can follow the prompts with whatever frameworks you’d like!

Now after doing the whole npm install thing, you’ll want to add a .local.env file and add in the environment variables you want to include. I want to have a name for the person who owns the site, their profile picture, and then all of their relevant links.

VITE_NAME=Cassidy Williams
VITE_PROFILE_PIC=https://github.com/cassidoo.png
VITE_GITHUB_LINK=https://github.com/cassidoo
VITE_TWITTER_LINK=https://twitter.com/cassidoo

You can set this up however you’d like, because this is just test data we’ll build off of! As you build out your own application, you can pull in your environment variables at any time for parsing with import.meta.env. Vite lets you access those variables from the client code with VITE_, so as you play around with variables, make sure you prepend that to your variables.

Ultimately, I made a rather large parsing function that I passed to my components to render into the template:

function getPageContent() {
  // Pull in all variables that start with VITE_ and turn it into an array
  let envVars = Object.entries(import.meta.env).filter((key) => key[0].startsWith('VITE_'))

  // Get the name and profile picture, since those are structured differently from the links
  const name = envVars.find((val) => val[0] === 'VITE_NAME')[1].replace(/_/g, ' ')
  const profilePic = envVars.find((val) => val[0] === 'VITE_PROFILE_PIC')[1]
  
  // ...
  
  // Pull all of the links, and properly format the names to be all lowercase and normalized
  let links = envVars.map((k) => {
    return [deEnvify(k[0]), k[1]]
  })

  // This object is what is ultimately sent to React to be rendered
  return { name, profilePic, links }
}

function deEnvify(str) {
  return str.replace('VITE_', '').replace('_LINK', '').toLowerCase().split('_').join(' ')
}

I can now pull in these variables into a React function that renders the components I need:

// ...
  return (
    <div>
      <img alt={vars.name} src={vars.profilePic} />
      <p>{vars.name}</p>
      {vars.links.map((l, index) => {
        return <Link key={`link${index}`} name={l[0]} href={l[1]} />
      })}
    </div>
  )

// ...

And voilà! With a little CSS, we have a “link in bio” site!

Now let’s turn this into something that doesn’t rely on hard-coded variables. Generator time!

The Generator project

I’m going to start a new Vite site, just like I did before, but I’ll be using Vue for this one, for funzies.

Now in this project, I need to generate the environment variables we talked about above. So we’ll need an input for the name, an input for the profile picture, and then a set of inputs for each link that a person might want to make.

In my App.vue template, I’ll have these separated out like so:

<template>
  <div>
    <p>
      <span>Your name:</span>
      <input type="text" v-model="name" />
	</p>
    <p>
      <span>Your profile picture:</span>	
      <input type="text" v-model="propic" />
    </p>
  </div>

  <List v-model:list="list" />

  <GenerateButton :name="name" :propic="propic" :list="list" />
</template>

In that List component, we’ll have dual inputs that gather all of the links our users might want to add:

<template>
  <div class="list">
    Add a link: <br />
    <input type="text" v-model="newItem.name" />
    <input type="text" v-model="newItem.url" @keyup.enter="addItem" />
    <button @click="addItem">+</button>

    <ListItem
      v-for="(item, index) in list"
      :key="index"
      :item="item"
      @delete="removeItem(index)"
    />
  </div>
</template>

So in this component, there’s the two inputs that are adding to an object called newItem, and then the ListItem component lists out all of the links that have been created already, and each one can delete itself.

Now, we can take all of these values we’ve gotten from our users, and populate the GenerateButton component with them to make our DTN button work!

The template in GenerateButton is just an <a> tag with the link. The power in this one comes from the methods in the <script>.

// ...
methods: {
  convertLink(str) {
    // Convert each string passed in to use the VITE_WHATEVER_LINK syntax that our template expects
    return `VITE_${str.replace(/ /g, '_').toUpperCase()}_LINK`
  },
  convertListOfLinks() {
    let linkString = ''
    
    // Pass each link given by the user to our helper function
    this.list.forEach((l) => {
      linkString += `${this.convertLink(l.name)}=${l.url}&`
    })

    return linkString
  },
  // This function pushes all of our strings together into one giant link that will be put into our button that will deploy everything!
  siteLink() {
    return (
      // This is the base URL we need of our template repo, and the Netlify deploy trigger
      'https://app.netlify.com/start/deploy?repository=https://github.com/cassidoo/link-in-bio-template#' +
      'VITE_NAME=' +
      // Replacing spaces with underscores in the name so that the URL doesn't turn that into %20
      this.name.replace(/ /g, '_') +
      '&' +
      'VITE_PROFILE_PIC=' +
      this.propic +
      '&' +
      // Pulls all the links from our helper function above
      this.convertListOfLinks()
    )
  },
}, 

Believe it or not, that’s it. You can add whatever styles you like or change up what variables are passed (like themes, toggles, etc.) to make this truly customizable!

Put it all together

Once these projects are deployed, they can work together in beautiful harmony!

This is the kind of project that can really illustrate the power of customization when you have access to user-generated environment variables. It may be a small one, but when you think about generating, say, resume websites, e-commerce themes, “/uses” websites, marketing sites… the possibilities are endless for turning this into a really cool boilerplate method.


The post Hack the “Deploy to Netlify” Button Using Environment Variables to Make a Customizable Site Generator appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

What’s Old is New

This year, I learned a lot about how “old” tricks can solve a lot of modern problems if you use the right tools. Following the growth of Jamstack-style development has been both a learning experience, while also a nostalgic one. It’s been amazing to see how you can power plain ol’ HTML, CSS, and JavaScript with the rise of headless CMSes, API-driven databases, e-commerce services, and modern frameworks.

I feel like the biggest hurdle that all of the different framework developers and hosting providers are trying to overcome is the fine art of caching. There are so many different approaches to how to serve the most performant, accessible, user-friendly, fast websites.

I love seeing the “hot takes” on this because some of them are old, some are new, and some are combining the old and the new into really interesting ideas.

Conversations around “stale-while-revalidate” and incremental static regeneration and hybrid applications are fascinating to me, and they’re all the right answer and the wrong answer depending on the project.

I’m very optimistic about the future of web development right now. There are a lot of smart brains experimenting with these technologies, and there’s a lot of education happening in the space right now. It reminds me of the phrase, “a rising tide lifts all boats.” We’re all trying to build the best websites we can right now, and though it might seem like it’s competitive, I’m very hopeful about how much we can be “lifted” together by collective learning.


The post What’s Old is New appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.