Component-based Web Development for Beginners

Website, It all starts with a simple index.html page. We can write anything and publishing it to the whole world is mind-blowing. Nobody believed it was the future. But here we are, living in the future. Web design is not about index.html anymore. It's a complex infrastructure with complicated servers, countless libraries, frameworks, and tools.

Everything I mentioned above was invented for a common goal: Save Time. In this article, I'm going to explain how Component-based approach speed up your web development using DRY Principle (Don't repeat yourself)

The Problem.

Imagine you have developed a simple website with few pages: "Home, About, Contact". Each page contains a Navbar, information for each page, and a footer.

Now you want to add another page called "Services". Easy. You create another page called services.html and add a link from other pages.

Now Imagine if this was a big project with 100s of pages. You would need to open 100 pages to add a link. Then you want the link to be first, Now you open the 100 pages again to make these changes. Sounds fun? No more. It's now become a boring repeatable task. And it's not scalable as our simple website.

The Solution

What if we could have a single Navbar page where we could update in one place and it automatically updates on 100s of these pages? Mind-blowing right? That's where the component-based approach comes into play.

Every modern framework like React, Next, Vue, Nuxt, Svelte, Astro, etc. supports this approach. We will see this through a live example by converting a normal page to a component.

Live Example

Here is a live example of how you can convert the following section to a component-based approach.

image.png

Look at the above example. Which is often seen on any website. Usually the section will be on your index page. Let's see how we can transform it using modern tools.

Existing Layout

<!DOCTYPE html>
<html lang="en">
<head> ... </head>
<body>
    <div>// nav, hero etc goes here</div>
    <div class="our-section">
          ...
          ...
    </div>
    <div>// cta, footer etc goes here. </div>
</body>
</html>

The first thing you should do is to move it to another page and import the same in your main page.

Here's how you would do it in React.

// components/features.js

export default function MySection() {
 return (
    <div> My section content </div>
)}
// pages/index.js

import MySection from "../components/features"

export default function Home() {
 return ( 
    <> 
      // nav, hero etc goes here
     <MySection>
      // cta, footer etc goes here.
    </>
)}

Here's the same example using Astro

Astro is a web framework used for static site generation focused on speed using less or no javascript.

<!-- components/features.astro -->

<div> My section content </div>
<!-- pages/index.astro -->
---
import MySection from "../components/features.astro"
---

<!-- Nav, Hero goes here -->

<MySection>

<!-- CTA, Footer goes here -->

We are not done. There is more

The first step is done, we have made sure it can be imported into any page. But there is still work pending. If you see the above image again, you can see that the feature is a two-column grid and shares the same design. So instead of repeating those four grids, we can use the data-driven component method to reduce even further.

Here's how you can do that in React.

  1. First, extract the repeatable data in to an array. In real project this might be coming from a headless CMS or an API. for demo, I will just put those as static array.
// components/features.js

import { MyIcon } from "lib/icons";

const features = [
  {
    title: "Competitive exchange rates",
    desc: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. ",
    icon: <MyIcon/>
  },
  {
    title: "No hidden fees",
    desc: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. ",
    icon: <MyIcon/>
  },
  {
    title: "Transfers are instant",
    desc: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. ",
    icon: <MyIcon/>
  },
  {
    title: "Mobile notifications",
    desc: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. ",
    icon: <MyIcon/>
  },
]

export default function MySection() {
 return (
 <>
  <div> // Section Title</div>
  <div>  
      {features.map((item, index) => (
             <div key={index}>
               <span>{item.icon}</span>
                <h3>{item.title}</h3>
                <p>{item.desc}</p>
             </div>
        ))}
</div>
</>
)}

Same example for Astro Static Site Generator.

// components/features.astro
---
import { MyIcon } from "assets/Icons.astro";

const features = [
  {
    title: "Competitive exchange rates",
    desc: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. ",
    icon: <MyIcon/>
  },
  {
    title: "No hidden fees",
    desc: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. ",
    icon: <MyIcon/>
  },
  {
    title: "Transfers are instant",
    desc: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. ",
    icon: <MyIcon/>
  },
  {
    title: "Mobile notifications",
    desc: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. ",
    icon: <MyIcon/>
  },
]
---

  <div> // Section Title</div>
  <div>  
      {features.map((item) => (
             <div>
               <span>{item.icon}</span>
                <h3>{item.title}</h3>
                <p>{item.desc}</p>
             </div>
        ))}
</div>

Further Customizations

You can go one step and pass the data as props. You can pass any data to this component to render. Here's a quick example with the title as props.

React Props Example

// pages/index.js

import MySection from "../components/features"

export default function Home() {
 return ( 
    <>  
     <MySection title="My Custom Section Title"> 
    </>
)}

Then on the component page:

// components/features.js

const features = [...]

export default function MySection(props) {
 return (
 <>
  <div>{props.title}</div>
  <div>  
      {features.map((item, index) => (
             ...
        ))}
</div>
</>
)}

Same with Astro:

// components/features.astro
---
const { title } = Astro.props;
---

  <div> {title} </div>
  <div>  
      {features.map((item) => (
             ...
        ))}
</div>

Rendering as child content

Both React & Astro support rendering content as children. Here's an example:

React Example

// pages/index.astro

import MySection from "../components/features"

export default function Home() {
 return ( 
    <>  
     <MySection> 
            <div>I can do anything here..</div>
     </MySection>
    </>
)}

Then you can render that content using {props.children}

// components/features.js

const features = [...]

export default function MySection(props) {
 return (
 <>
  <div>  
      {props.children}
</div>
</>
)}

Astro Example

<!-- pages/index.astro -->
---
import MySection from "../components/features.astro"
---

<MySection>
      <div>I can do anything here..</div>
 </MySection>

Then, using <slot/> we can render the content inside the component.

<!-- components/features.astro -->

 <div>   
      <slot/>
</div>

Hope you have learned something new. This is the best way to approach modern web development which is scalable and easy to maintain. Let me know your questions.

Did you find this article valuable?

Support Surjith S M by becoming a sponsor. Any amount is appreciated!