UpNEXT: To NODE is to REACT 2/4

UpNEXT: To NODE is to REACT 2/4

Building the Front-End (UI) on the Go (Baptism by Fire🔥)

·

11 min read

As I embarked on my journey to build a Next.js app, little did I know that I was about to dive headfirst into the vast ocean of front-end development. Armed with curiosity and a willingness to learn, I set out to conquer the realm of UI design. In this article which is a second continuation of my 4 part series on my Baptism by Fire into React, I'll share my experiences and discoveries, highlighting the interesting twists and turns I encountered along the way. If you just catching up you can check out my previous article: UpNext: To NODE is to REACT 1/4 before embarking on this one.

The first hurdle I faced was designing the UI using Tailwind CSS. Fortunately, the Tailwind CSS website had an extensive and well-organized documentation section that proved to be an invaluable resource. With its help, I quickly grasped the basics and learned how to put together visually pleasing designs. Tailwind CSS provided a range of utility classes that made it easy to add styles to elements, and I found myself experimenting with various combinations to create unique and appealing interfaces.

Ways of building consistent UI with Next.js

In a Next.js app, there are several ways I learnt that you could use to declare a consistent user interface (UI) across your application. Here are a few commonly used methods:

  1. Layout Components: Next.js allows you to create layout components that wrap around your pages and provide a consistent structure for your UI. You can define a layout component that includes common elements like header, footer, navigation, and so on. Each page can then be wrapped with this layout component to ensure a consistent UI across all pages.

  2. CSS-in-JS: Next.js has built-in support for CSS-in-JS libraries like Styled Components, Emotion, and CSS Modules. These libraries allow you to define styles directly in your JavaScript or TypeScript files. By encapsulating the styles within your components, you can ensure that the UI remains consistent and self-contained.

  3. Theme Providers: You can use theme providers to define a set of styles or variables that can be accessed throughout your application. This approach helps maintain consistency by centralizing common styles, colours, and typography. Libraries like Material-UI and Chakra UI provide theme providers that can be easily integrated into Next.js apps.

  4. Component Libraries: Utilizing component libraries can provide a consistent UI design across your Next.js app. These libraries offer pre-designed and pre-styled components that you can use throughout your application. Examples of popular component libraries include Material-UI, Ant Design, and Tailwind CSS.

  5. CSS Frameworks: Next.js works seamlessly with CSS frameworks like Bootstrap or Tailwind CSS. By integrating a CSS framework, you can leverage their pre-built styles and components to maintain consistency in your UI. These frameworks usually provide comprehensive documentation on how to integrate them with Next.js.

  6. Global Styles: If you prefer traditional CSS, you can create global CSS files in your Next.js app. You can import and apply these styles globally to ensure consistent UI across all components and pages. However, be mindful of potential style conflicts and specificity issues when using global styles.

Remember that choosing the right approach depends on your project's requirements, team preferences, and existing codebase. It's important to strike a balance between consistency and flexibility to ensure a smooth and maintainable UI in your Next.js app.

The Magic of Tailwind CSS

So I needed to achieve a sticky navigation bar like that one when the site is accessed on a desktop. All I would need was to make use of the elements below:

  • fixed: This class applies the position: fixed property to the nav element, making it fixed in the viewport. It will remain in a fixed position even if the user scrolls the page.

  • top-0: This class positions the nav element at the top of its containing element. The 0 value represents the distance from the top.

  • z-10: This class sets the z-index of the nav element to 10, which determines its stacking order relative to other elements on the page. A higher value means it will appear above elements with a lower z-index.

  • w-full: This class sets the width of the nav element to 100%, making it span the entire width of its containing element.

  • px-2 and md:px-5: These classes set the horizontal padding (left and right) of the nav element. px-2 applies to screens smaller than the medium breakpoint, while md:px-5 applies to screens equal to or larger than the medium breakpoint. The values (2 and 5) represent the number of spacing units provided by Tailwind CSS.

  • py-1: This class sets the vertical padding (top and bottom) of the nav element. The value 1 represents the number of spacing units.

  • shadow: This class adds a shadow effect to the nav element, providing a visual depth and distinction from the underlying content.

  • bgNav: This class sets a custom background colour for the nav element. It likely refers to a user-defined or theme-specific class that assigns a specific colour value.

  • bg-gradient-to-r from-blue-500 to-indigo-500: This class applies a gradient background to the nav element, transitioning from blue-500 to indigo-500. This creates a horizontal gradient effect from left to right.

All these when put together as code would look like this:

<nav className="fixed top-0 z-10 w-full px-2 py-1 shadow bgNav md:px-5 bg-gradient-to-r from-blue-500 to-indigo-500">
</nav>

And the beauty of it is that we have lots of Tailwind playgrounds starting from the official one by Tailwind where you can design your UI to your taste before moving it to your app! We even have some premium service providers where you can do more from building a UI you want to save drafts or actually picking templates there. And don't you forget that we also have AI tools around to make our Front-End work seamless.

Tailwind make your app Responsive

A front-end app is not any good if it is not responsive on smaller devices. With a few classes that will control the visibility and behaviour of certain components the responsive ui can be achieved using the following code snippet:

import React, { useState } from 'react'

export default function NavBar({ }) {
  const [navbar, setNavbar] = useState(false)

  return (
    <>
      <nav className="fixed top-0 z-10 w-full px-2 py-1 shadow bgNav md:px-5 bg-gradient-to-r from-blue-500 to-indigo-500">
        <div className="justify-between px-4 mx-auto md:flex md:items-center md:px-8">
          <div>
            <div className="flex items-center justify-between py-2 md:py-2">
              <Link href="/">
                <Image src="/applogo.png" className="cursor-pointer" height={50} width={50} alt="logo" />
              </Link>

              <div className="md:hidden">
                <button
                  className="p-3 rounded-md outline-none focus:border focus:border-gray-400"
                  onClick={() => setNavbar(!navbar)}
                >
                  {navbar ? (
                    <svg xmlns="http://www.w3.org/2000/svg" className="w-6 h-6 text-black" viewBox="0 0 20 20" fill="currentColor" >
                      <path
                        fillRule="evenodd"
                        d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
                        clipRule="evenodd"
                      />
                    </svg>
                  ) : (
                    <svg
                      xmlns="http://www.w3.org/2000/svg"
                      className="w-6 h-6 text-black"
                      fill="none"
                      viewBox="0 0 24 24"
                      stroke="currentColor"
                      strokeWidth={2}
                    >
                      <path strokeLinecap="round" strokeLinejoin="round" d="M4 6h16M4 12h16M4 18h16" />
                    </svg>
                  )}
                </button>
              </div>
            </div>
          </div>
          <div>
            <div className={`mt-8 flex-1 justify-self-center pb-3 md:mt-0 md:block md:pb-0 ${navbar ? "block" : "hidden" }`}>
              <input
                className="w-full py-2 pl-10 pr-4 rounded-full bg-gray-100 border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
                type="text" placeholder="Search"
              />
              <div className="flex flex-col mt-4 space-y-5 items md:hidden">
                <div className="flex items-center p-1 cursor-pointer" >
                  <div className="w-full py-2 pl-1 text-white"> CREATE </div>
                </div>
              </div>
            </div>
          </div>
          <div className="hidden md:flex md:items-center">
            <div className="flex items-center p-1 cursor-pointer" >
              <button className="px-4 py-2 font-semibold text-sm bg-sky-500 text-white rounded-full shadow-sm border-2 border-white flex items-center">
                <FaPlus className="mr-1" /> CREATE
              </button>
            </div>
          </div>
        </div>
      </nav>
    </>
  )
}

Let's go through the specific classes and code used in the provided code snippet:

  1. md:hidden: This class is used to hide the element on screens larger than the medium breakpoint (md). In this case, it is applied to the div containing the button. This ensures that the button is hidden on larger screens and only shown on smaller screens when the mobile menu is active.

  2. block and hidden: These classes are used to control the visibility of an element. In this case, they are used in conjunction with the navbar state variable to show or hide the mobile menu. When navbar is true, the element with the class block is displayed, and when navbar is false, the element with the class hidden is displayed. This allows toggling the visibility of the mobile menu based on the navbar state.

  3. const [navbar, setNavbar] = useState(false): This line of code uses the useState hook to create a state variable navbar with an initial value of false. The setNavbar function is used to update the value of navbar. It is used to control the visibility of the mobile menu by toggling the navbar state between true and false when the button is clicked.

By combining these classes and code, the provided code snippet achieves the following functionality:

  • On smaller screens, the button with the class md:hidden is displayed, allowing the user to toggle the visibility of the mobile menu by clicking on it.

  • When the mobile menu is active (i.e., navbar is true), the input field with the search placeholder is displayed (block class) along with the "CREATE" button.

  • On larger screens, the mobile menu is hidden (hidden class), and the "CREATE" button is displayed in the right-hand section of the navigation bar.

This code snippet demonstrates how Tailwind CSS classes, along with state management using the useState hook, can be used to create responsive and interactive UI components in a Next.js application.


Components, just like Widgets in Flutter!

One aspect that fascinated me was the concept of components, reminiscent of Flutter's widgets. I discovered that I could create reusable components in Next.js, allowing me to streamline my development process. These components could accept parameters, enabling dynamic customization.

For example, I created a Button component that could receive different text labels, colours, and even event handlers. Here's a snippet of how it looked:

import React from 'react';

const Button = ({ label, color, onClick }) => {
  return (
    <button className={`bg-${color}-500 hover:bg-${color}-700 text-white font-bold py-2 px-4 rounded`} onClick={onClick}>
      {label}
    </button>
  );
};

export default Button;

I was thrilled to see how these components could be reused throughout my app, providing consistency and efficiency in my UI development especially using Tailwind CSS. For those wondering about Tailwind, it's a utility-first CSS framework that provides a comprehensive set of pre-built classes for building user interfaces.

Oops! Not everything could be Tailed!

However, not all designs were easily achieved with Tailwind CSS alone. While it offered a wide range of utility classes to style and structure your UI, there are some elements that it didn't specifically include or target. Here are a few examples:

  1. Custom Components: it doesn't provide pre-styled components for complex UI elements like modals, tabs, or carousels. It focuses on utility classes for low-level styling, leaving the customization of components to developers.

  2. JavaScript Functionality: it's solely a CSS framework and does not include any JavaScript functionality. You'll need to use separate JavaScript libraries or frameworks like React, Vue.js, or Alpine.js to add interactivity and dynamic behaviour to your components.

  3. Icons: While it offers utility classes for manipulating icons (such as sizing and positioning), it does not provide a built-in set of icons. You'll need to use an icon library like Font Awesome, Heroicons, or SVG icons to include icons in your UI.

  4. Form Validation: it doesn't provide built-in form validation functionality. You'll need to implement form-validation logic using JavaScript or utilize form-validation libraries like Formik or Yup.

  5. Responsive Images: Although it includes utility classes for responsive design (e.g., responsive breakpoints), it does not specifically handle responsive images. You'll need to handle image responsiveness separately, using techniques like CSS media queries or a responsive image library.

It's worth noting that while Tailwind CSS may not have built-in support for certain elements or functionalities, it provides a flexible and customizable foundation upon which you can build your UI. You can always extend Tailwind CSS by creating custom utility classes or integrating them with other libraries or frameworks to fulfil specific requirements.

As I delved deeper into building responsive designs, I discovered there were numerous approaches to consider. One technique that stood out was building a skeleton in the _app.tsx file. This approach involved creating a loading state for the entire application, providing users with a visually pleasing and responsive experience while the content loaded. By implementing this technique, I could enhance the perceived performance of my app and keep users engaged during loading times.

Smart Pages Declaration!

Next.js also presented various ways to structure and implement pages. For instance, I could create a simple page by defining a component inside the pages directory, such as pages/About.js. This would automatically generate the route "/about" in my app. Alternatively, I could utilize the getStaticProps and getServerSideProps functions to fetch data dynamically and pre-render pages for enhanced performance. This flexibility allowed me to tailor the implementation to the specific requirements of my app.

My journey into building UI on a Next.js app has been an exhilarating one. Through the exploration of Tailwind CSS, the concept of reusable components, the integration of additional plugins, and the adoption of responsive design techniques, I've grown as a front-end developer. The ability to adapt and experiment with various implementation strategies in Next.js has opened up a world of possibilities for me.

As I continue to refine my skills, I look forward to further honing my UI development expertise and exploring the ever-evolving landscape of front-end technologies. With each line of code, I write, I am reminded that the art of building beautiful and functional user interfaces is a journey, and I'm excited to see where it takes me.


In the next article, I talk about my back-end experiences in doing a React project using Express.js and MongoDb

Â