DEV Community

Cover image for How I developed a modern JAMStack website
Joseph Mukorivo
Joseph Mukorivo

Posted on • Edited on • Originally published at josemukorivo.com

How I developed a modern JAMStack website

Overview

In 2021 I started working on a rebranding project for a company that I was working for in 2020. Here is a link to the project. The company already had a nice website but they have been using that website since 2018 and they wanted something new and modern that is also easy to manage for non-developers. For this project to be a success I was working with a designer friend. We sat down and started planning how we were going to go about it because even if this was a company website it had a lot of moving parts so it needed some planning.

Figma Design

Image description
We used figma for every single component that we developed.

Homepage Design

Image description
Myself and my friend already had some experience with scrum for project management, so we decided to use scrum for this project since it was good fit, so we started to create a product backlog, prioritized features and divided the work into sprints. We were working very closely with the product owners to make sure that we were developing what the user really wanted.

Tech stack choice

So this was one of the interesting stages in the development of the project. We decided to use the JAMStack on this project for quite a number reasons. The design for the project was done using figma then I started the development of the UI using React for the UI. I decided to use tailwind css for styling because it makes it super fast to style components. I did not like the idea of having a bunch of classes on my markup so I used tailwind with css modules(I will show the snippets for some of the code). Because we wanted this website to be performant and SEO friendly I decided to use NextJS(The authors call it a React framework for production and they are right). NextJS has many features for performance and SEO out of the box like Server side rendering, Code splitting, optimized images, routing and many more. On this project it didn't make sense to spin up a custom api for the backend so I decided to use a modern CMS which in this case was strapi. All of the backend stuff on this site is coming from strapi.

There are also number of other tools I used but I will not go into the details of those. Below I will give a summary of key things I used.

Key things used in the project

React for the UI
NextJS for SSR/CSR/Routing and more
Tailwindcss for UI styling
Strapi as CMS
Docker for application containerization
nginx as a web server and reverse proxy
git for version control
mailchimp for managing a mailing list

Project Structure

For project structure I followed this structure with some improvements but was good as a starting point. Here is a high level overview.

Image description

Creating components

I tried to make the components that I developed reusable, Below are sample files for the Text component.

Text.css

.root {
  @apply mb-4;
}
.root:is(h1, h2, h3, h4, h5, h6) {
    @apply mb-7 2xl:mb-10 leading-tight dark:text-slate-200;
 }

.p {
  @apply text-lg 2xl:text-xl;
}
.span {
  @apply text-lg;
}

.h1 {
  @apply text-4xl md:text-5xl font-heading font-medium uppercase;
}

.h2 {
  @apply text-2xl md:text-4xl font-heading uppercase;
}

.h3 {
  @apply text-3xl;
}

.h4 {
  @apply text-2xl;
}

.h5 {
  @apply text-xl;
}

.h6 {
  @apply text-lg;
}

Enter fullscreen mode Exit fullscreen mode

Text.tsx

import { FC, CSSProperties, ReactNode } from 'react';
import cn from 'classnames';

import s from './Text.module.scss';

type Variant = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span';

interface Props {
  as?: Variant;
  className?: string;
  style?: CSSProperties;
  children?: ReactNode | any;
  html?: string;
}

export const Text: FC<Props> = ({
  as: Tag = 'p',
  className = '',
  style = {},
  children,
  html,
  ...rest
}) => {
  const classes = cn(
    s.root,
    {
      [s.p]: Tag === 'p',
      [s.span]: Tag === 'span',
      [s.h1]: Tag === 'h1',
      [s.h2]: Tag === 'h2',
      [s.h3]: Tag === 'h3',
      [s.h4]: Tag === 'h4',
      [s.h5]: Tag === 'h5',
      [s.h6]: Tag === 'h6',
    },
    className // make sure to add the className last so it overwrites the other classes
  );

  const htmlProps = html
    ? {
        dangerouslySetInnerHTML: { __html: html },
      }
    : {};

  return (
    <Tag className={classes} {...rest} {...htmlProps}>
      {children}
    </Tag>
  );
};
Enter fullscreen mode Exit fullscreen mode

Example Usage

<Text as='h1'>
 Hi 👋🏼, I’m Joseph. Writer, Software Engineer, DevOps
</Text>

<Text className='cool'>
This is a sample paragraph
</Text>
Enter fullscreen mode Exit fullscreen mode

Hosting and deployment

The company I developed this website for is not a big company and they don't have a big tech team so I used the tools that I thought were easy for someone else to maintain. Strapi is running as a docker container using docker-compose, the frontend is also running in a similar way. In the source code for both strapi and the frontend I created some Make files to run the project. Below is a sample Makefile.

down:
    docker-compose down

build:
    docker-compose up -d --build

redeploy:
    git pull && make down && make build
Enter fullscreen mode Exit fullscreen mode

In this case if there are changes to the source code, the user does not need to know how to use docker, they just run make redeploy in the root of the project and all the code pulling and image building is done for them, of course this is clearly labelled in the README.

So these services are running on different ports on the server and I exposed them using nginx. Below is how one may configure their nginx file for strapi. Please Note on production you have to make sure that you do it in a secure way this is just to help you get started.

server {
   server_name example.com www.example.com;

    location / {
        keepalive_timeout 64;
        proxy_pass http://localhost:8080; # use your local port where strapi is running
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-NginX-Proxy true;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_pass_request_headers on;
        proxy_max_temp_file_size 0;
        proxy_redirect off;
        proxy_read_timeout 240s;
    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot


}
server {
    if ($host = www.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


   server_name example.com www.example.com;
    listen 80;
    return 404; # managed by Certbot
}
Enter fullscreen mode Exit fullscreen mode

I hope this article helped you. P.S you can follow me on twitter.

Photo by Hal Gatewood on Unsplash

Top comments (23)

Collapse
 
chasm profile image
Charles F. Munat

Here's what I'm using for the web application I'm building:

HTML
CSS
JS (TS)

Works like a charm. Very, very fast. Totally accessible. No worries about the security of other devs' code or any dependency on them to test well, stay current, etc. And the code is actually a lot simpler, but the user can't tell the difference (except that it's faster and more responsive).

The tools you think are saving you time will cost you time sooner than you think. And time is money. And the time you waste learning Tailwind and React, etc. is time you can't spend becoming expert at what actually works in the browser: HTML, CSS, and JS. How well do you really know any of those three? And yet they are the only languages the browser actually cares about.

We've rushed headlong down the tunnel of cool toys so far and so fast that we can no longer remember what it was we were looking for. Now, it's all about the toys.

Collapse
 
josemukorivo profile image
Joseph Mukorivo

Thanks for the insight, but at the same time I also there are times when using these tools will be good, like if you have time constrains using something like tailwind for styling will be faster than writting custom css since you wont have to worry much about things like cross browser compatibilty.... React also helps in other things like sharing code easily by creating reusable components...

But in general I think the tools we use for a given project should differ depending on the constrains we have.

Collapse
 
chasm profile image
Charles F. Munat

Yes, constraints determine the tools, but that's the point. The real constraint here is the serious lack of front-end developers who actually know their tools: HTML, CSS, and JS. Most of these other tools of which you speak were invented to prevent devs from ever having to learn their true craft. How sad.

Become an expert in HTML and CSS first, then see if you really need anything else. You might, but it certainly won't be a huge stack of overly complex libraries and frameworks.

Collapse
 
gi_dev profile image
G_dev

"And the time you waste learning Tailwind and React"

Learning React isn't a waste of time, we use it a lot at work.

Collapse
 
chasm profile image
Charles F. Munat

Actually, I have to use it at work, too. And from that I know that this only multiplies the waste. It is true that from the perspective of finding a job knowing React or similar will help greatly. But how sad is that?

We have complicated software up to absurd and obscene levels and pretended that it was because we were doing "enterprise" work. But the more complex the software, the more difficult and time consuming it is to build and maintain, the less secure it is, and the more it tends to break.

So the fact that "everyone" is doing it and so we all have to jump on the bandwagon, too, if we want to make good money in dev is not a reason to cheer, but a sad waste of time, money, and most importantly resources on a planet with an ever-dwindling supply.

My philosophy is simple: don't build anything until you have to, and build it as simply and cleanly as possible. Of course, to most devs this is pure heresy.

Thread Thread
 
gi_dev profile image
G_dev

Plain html/css/js would just make my work more stressful than it already is.

Maybe for a personal project I would consider it. But it would have to be really simple. Building a complex front end without a framework would be setting yourself up for a headache.

Thread Thread
 
josemukorivo profile image
Joseph Mukorivo

Totally agree, especially if you don't work on just one project.

Thread Thread
 
chasm profile image
Charles F. Munat

That's what you think, which is pretty much what every other dev in the herd thinks. Why would you be any different? And it seems true to herd animals. It's those mirror neurons. Hooray for the bandwagon!

But how many websites have you actually built -- reasonably functional sites (if they are very complex then they are too big) -- using just vanilla JS/CSS/HTML? Have you ever built any? Post a link!

If the answer is no then you are just bloviating. You have no data from which to draw these conclusions. And if you say you "tried" it once or twice, well, I bet you beat your head against the wall for a long time trying to build React apps before you succeeded. I sure did. Did Webpack just magically work for you? I practically have PTSD from messing with Webpack. Why would you expect to learn anything new to you with no effort?

When you make these claims then you are forgetting just how painful learning React, various build systems, IDEs, state management, etc. was. Remember all the brouhaha when React hooks came out about how they would eliminate the "pain" of Redux? And have they, really? Or have they just replaced one pain with another? useEffect is a bear to learn. Working with the actual DOM via React is still painful. And most devs are still using hooks wrongly. Just look at all the clickbait crap on Medium telling you how to "do it right".

Building web apps isn't getting easier, it's getting more complex and difficult. Layer upon layer upon layer. Don't they call it "JavaScript hell"? But is it really hell to just write exactly the code your site needs?

So you compare the current methodology of libraries and frameworks galore -- one in which you have invested an enormous amount of painful effort to learn -- with one that you've never really tried, if you're honest. Had you invested that same effort in actually learning HTML and CSS and the "good parts" of JS, isn't it possible that you'd be able to build state-of-the-art websites (all of which, no exceptions, are built with HTML, CSS, and JS and only HTML, CSS, and JS) just as quickly and easily as using these monstrous libraries and frameworks?

And with no dependence on other devs' code (and hygiene), no concerns about version conflicts, no need for regular house cleaning and upgrading, no big bulky bandwidth-hogging downloads? Oh, and no "magic" between you and your code, so no mystery as to how it all works.

So essentially what both of you are doing is simply waving to your fellow herd members and saying "Don't worry! We won't upset the apple cart." It's about optics and belonging, not reality.

And it's a shame, because these frameworks and libraries are not moving FE design forward, they are blocking it. They are a terrible drag, and each new example meant to "fix" the problems caused by the previous one (which at the time was heralded as the great new way around the problems of the ones it replaced) only introduces new problems and takes us further off course.

The best possible web application a) has no features not needed by the user, b) has no gratuitous design nonsense, c) loads no code that isn't specific to the app (no dependencies on anything not already in the user agent), d) includes no code that isn't absolutely necessary for the functionality of that page, and e) is semantically correct and includes appropriate metadata and security measures.

The smartest thing any dev ever said to me was, "Never write a line of code until you have to." The same applies to dependencies. Never add a dependency until you absolutely have to. You'd be surprised how often the big panacea tool everyone is hyping isn't really a benefit at all.

I get it that this is heresy to all the boys and their toys, but I think the evidence will, in the end, bear me out.

This isn't quite the way I would do it (I think web components are largely unnecessary, though I love the event bus), but it shows that it can be done easily: Real World Vanilla JS Even Driven Web Components.

Collapse
 
ozzythegiant profile image
Oziel Perez • Edited

I personally use Gridsome for static websites. It's Vue based and creates pure HTML files with javascript files on the side. For react you may want to look at Gatsby (the react equivalent of Gridsome) as it's more perfomant for websites with a lot of content (dozens or even hundreds of pages). No need to run Node.js in the back end, at least not to serve web pages and unless your CMS is Node.js based. In such a case, I recommend Directus or Strapi as the CMS

Collapse
 
josemukorivo profile image
Joseph Mukorivo

I used NextJS on this one.The site is not really 100% static, there a couple of pages with dynamic content. NextJS is a good choice in those situations. Will check gridsome I happen to learn Vue.

Collapse
 
divofred profile image
Fredrick Emmanuel

I think you should add what JAMStack is.
Just a suggestion

Collapse
 
josemukorivo profile image
Joseph Mukorivo

Thanks will update the post accordingly.

Collapse
 
andrewbaisden profile image
Andrew Baisden

Great article the Figma + React tech stack is so popular now. My latest website was built with it too.

Collapse
 
omarpervez profile image
Omar Pervez

You did a Great Job. ☺️

Collapse
 
josemukorivo profile image
Joseph Mukorivo

Thanks Omer

Collapse
 
omarpervez profile image
Omar Pervez

Welcome Joseph

Collapse
 
juanvegadev profile image
Juan Vega

Hi Joseph, did you evaluate remix.run? I am currently checking which framework learn next and I'd appreciate insights of nextjs vs remix here

Collapse
 
josemukorivo profile image
Joseph Mukorivo

Hi Juan, I looked at remix.run docs I think once or twice so my response on that will probably be biased. But I think as of now nextjs has the advantage of being mature so there is a lot of developer tooling. You can try nextjs

Collapse
 
juanvegadev profile image
Juan Vega

Fair point, thanks for answering

Collapse
 
mathieuhuot profile image
Mathieu Huot

Nice exemple of a real world full stack Jamstack! May I ask what you use for hosting? I usually see this type of architecture with different hosting for frontend and backend. Just curious. Thanks for sharing! 😄

Collapse
 
josemukorivo profile image
Joseph Mukorivo

Both the frontend and backend are hosted on a private server, but if your users are distrubuted across the the entire globe or maybe continent using something like netlify or vercel will be good they have good CDNs.

Collapse
 
xsteacy profile image
Steacy Paquette

You could also use Cloudflare or GitHub pages for the frontend (free).
Heroku for the backend so you can easily use a database for Strapi. (Free too but paid options)
Just other suggestions.

Collapse
 
josemukorivo profile image
Joseph Mukorivo

Thanks, but the client wanted to use their own infastructure. I use Vercel for my personal projects.