DEV Community

Cover image for How to use Styled Components to Customize Your Blog.
Zachery Morgan
Zachery Morgan

Posted on • Edited on

How to use Styled Components to Customize Your Blog.

This is lesson 7 in a tutorial on how to create a blog using NextJS, Sanity, and Vercel.
If you are just here for help with Styled Components then you will need to implement your own text for the elements or follow my guide from lesson 1.

  • I also have a link to a working sandbox of this code in my Preview section that includes text data you can use instead.
  • I didn't implement responsiveness for small screens. I set up the entire post information in a grid so implementing it won't be difficult. I'm leaving it up to you to create your own unique mobile view!

I've done some tweaking to the props we fetch. I will explain that part after the Planning, so if you just want Styled Components, you can skip ahead.
You should also create additional posts (at least have 2 in total) to see our Recent Posts sidebar.


Planning

It is never a good idea to style from your code. What I mean is, you should have an idea ahead of time on how you want things to look (even sketch it out), then replicate that with your code.

On the topic of 'sketching it out', I did just that. In only a couple of minutes I was able to create this Figma design.

You might need to signup to view it if you don't have a Figma account, but it's worth it to have one. The amount of time you save by sketching things out cannot be understated.

I didn't make the most complex UI ever, but it's perfectly fine for our needs. You can also click each element and see it's name (purpose) on the left hand side of the screen under Layers > Desktop-1.

One of the elements is called Social Media Buttons, so we are going to go ahead and install an npm package that is standard for almost every project.

In the command line of your project, run

npm i react-icons --save &&
npm i react-portable-text
Enter fullscreen mode Exit fullscreen mode

Reading the React Icons docs will explain how to use it and let you search for any icon you want. All we have to do is find the code for the icon we want, import it, and insert it like a React component.

React Portable Text is what we are going to use to display the body value we couldn't use in the last lesson.


Table of Contents

I don't know of the best way to show all of my code. I don't want to host it incase it changes later, so unfortunately the best way I can think of is to just slap it here in blocks.

But I am going to break it down into 5 parts.

  1. Imports
    • The couple of imports we need for our page
  2. Styled Components
    • Our Styled Components (done in file, not imported)
  3. HTML Structure
    • Our HTML layout with content
  4. Props
    • Changes to our getStaticProps
  5. Preview
    • Online sandbox so you can see how it should look or copy the /pages/posts/[slug].js file.

Imports

This can look different for you if you choose to use additional icons, I'm just importing 3 basic ones for now.

import PortableText from "react-portable-text";
import styled from "styled-components";
import Link from "next/link";
import { BsFacebook, BsTwitter, BsInstagram } from "react-icons/bs";
Enter fullscreen mode Exit fullscreen mode

We import..

  • PortableText to display the body of our post.
  • styled so we can create styled components.
  • Link to navigate to the homepage or to other posts.
  • The last line is importing a Facebook, Twitter, and Instagram icon from our react-icons.

Styled Components

FYI: I only spent a little over an hour on the styling. It isn't perfect and some of the code is redundant, but I'm already behind on this project so I just wanted to get something that looked nice in browser.

Before we get to the components, I added a couple things to our /styles/globals.css file.

@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');

* {
  padding: 0;
  margin: 0;
  font-family: 'Poppins';
  color: #333;
}

body {
  background: rgb(248, 248, 248)
}

a {
  color: inherit;
  text-decoration: none;
}

* {
  box-sizing: border-box;
}
Enter fullscreen mode Exit fullscreen mode
  • Poppins is now our default font for everything.
  • #333 is the default font color for everything.
  • The body now has a background color of rgb(248, 248, 248).
    • This is an off white color that is easier on the eyes.

Below your imports but above your Post component is where we will create our styling. Normally this is done in separate files, but since we aren't styling that many pages I don't see a reason to break it up.

I explained Styled Components very briefly at the start of these guides, but I will go over 1 I created for this page as a refresher.

Do not copy this into your file yet, it will be included in the full list of components I will post after this explanation.

const BackButton = styled.div`
  border: 1px solid #333;
  border-radius: 7px;
  text-align: center;
  font-size: 1.2rem;
  width: fit-content;
  padding: 4px 6px;
  transition: all 0.3s;

  :hover {
    background: #333;
    color: white;
    cursor: pointer;
  }

  span {
    position: relative;
    bottom: 3px;
  }
`;
Enter fullscreen mode Exit fullscreen mode
  • First we create a component with the name of BackButton

    • This is a React component so it must be Pascal case (each word capital).
  • We declare that this component is a styled.div

    • styled. is from our import
    • div is letting React know what kind of HTML element we are assigning. in this case it will be a <div></div>
    • All you have to do to use it is replace <div></div> in your code for <BackButton></BackButton> and it will now be a div element with styling.
  • You also need to be aware that we are wrapping the CSS for our component in backticks.

  • Now inside is our basic CSS. I'm not going to go over the details of my CSS, or how I'm using grid for positioning because it's better for you to play around with it and find out yourself. But if you still do not understand the purpose of a line, you can either Google it, play around with it in your Browser DevTools, or even shoot me a message on Twitter and I can help explain further.

    • We apply styling to our div element
    • What happens when the div is hovered
    • And finally we apply styling to any span elements inside of this div.

After that quick example, here is the entire block of code for all of our styling. Remember, paste this below your imports and above your Post component.

const PageContainer = styled.div`
  padding: 70px 110px;
  max-width: 1600px;
  margin-inline: auto;
`;

const BackButton = styled.div`
  border: 1px solid #333;
  border-radius: 7px;
  text-align: center;
  font-size: 1.2rem;
  width: fit-content;
  padding: 4px 6px;
  transition: all 0.3s;

  :hover {
    background: #333;
    color: white;
    cursor: pointer;
  }

  span {
    position: relative;
    bottom: 3px;
  }
`;

const ContentContainer = styled.div`
  display: grid;
  grid-template-columns: 0.5fr 2fr 1fr;
  grid-template-rows: 1fr auto;
  grid-template-areas:
    "header1 header header2"
    "socials main links";
`;

const Header = styled.header`
  grid-area: header;
  text-align: center;
  margin-bottom: 40px;

  h1 {
    text-transform: uppercase;
    color: #222222;
    font-size: 3rem;
  }

  span {
    color: #464646;
  }
`;

const SocialsContainer = styled.ul`
  grid-area: socials;
  display: flex;
  flex-direction: column;
  align-items: end;
  list-style-type: none;
  gap: 30px;
  padding-right: 50px;
  padding-top: 14px;

  li {
    cursor: pointer;
  }
`;

const StyledLi = styled.li`
  svg {
    transition: transform 0.4s;

    path {
      color: #4040ac;
    }
  }

  svg:hover {
    transform: scale(1.2);
  }
`;

const Body = styled.main`
  grid-area: main;

  h1 {
    text-align: center;
    font-size: 2.3rem;
    margin-bottom: 15px;
    padding-right: 160px;
  }

  h2 {
    margin: 5px 0px;
  }

  div {
    display: flex;
    flex-direction: column;
    gap: 5px;
  }
`;

const RecentPostsContainer = styled.div`
  padding-left: 50px;
  grid-area: links;
  display: flex;
  flex-direction: column;
  gap: 20px;
`;

const RecentPost = styled.div`
  display: flex;
  flex-direction: column;
  border: 1px solid #333;
  align-items: center;
  transition: all 0.4s;

  p {
    font-size: 1.1rem;
    font-weight: 500;
  }

  span,
  p {
    transition: all 0.4s;
  }

  :hover {
    background: #333;
    cursor: pointer;

    span,
    p {
      color: white;
    }
  }
`;
Enter fullscreen mode Exit fullscreen mode

I'm styling the post.body content from within the Body component.
As you can see I only targeted h1, h2, and div because those are what I have inside of my post's body. If you have other elements you can style them how you see fit, by targeting the type of the element and applying your style.


HTML Structure

Pasting all of that in won't do a darn thing until you add these components to your code.

Remember, your code will break because I changed how we import our posts. That will be explained next.

export default function Post({ post, posts }) {
  const postsElements = posts.map((post, index) => (
    <Link key={index} href={`/posts/${post.slug}`}>
      <RecentPost>
        <p>{post.title}</p>
        <span>{new Date(post.publishedAt).toDateString().slice(4)}</span>
      </RecentPost>
    </Link>
  ));

  return (
    <PageContainer>

      <Link href={`/`}>
        <BackButton>
          <span>👈</span>
          <a>Home</a>
        </BackButton>
      </Link>

      <ContentContainer>

        <Header>
          <span>{new Date(post.publishedAt).toDateString()}</span>
          <h1>{post.title}</h1>
        </Header>

        <SocialsContainer>
          <StyledLi>
            <a href="https://facebook.com">
              <BsFacebook size={"3rem"} />
            </a>
          </StyledLi>
          <StyledLi>
            <a href="https://twitter.com">
              <BsTwitter size={"3rem"} />
            </a>
          </StyledLi>
          <StyledLi>
            <a href="https://instagram.com">
              <BsInstagram size={"3rem"} />
            </a>
          </StyledLi>
        </SocialsContainer>

        <Body>
          <PortableText content={post.body} />
        </Body>

        <RecentPostsContainer>
          {postsElements}
        </RecentPostsContainer>

      </ContentContainer>

    </PageContainer>
  );
}
Enter fullscreen mode Exit fullscreen mode

This is pretty simple, we are using our Styled Components we just created instead of normal HTML elements.

I will point out the couple of things that are important to take note of here.

export default function Post({ post, posts })
Enter fullscreen mode Exit fullscreen mode
  • You can see we are now importing post AND posts. You will see why in a second.
const postsElements = posts.map((post, index) => (
    <Link key={index} href={`/posts/${post.slug}`}>
      <RecentPost>
        <p>{post.title}</p>
        <span>{new Date(post.publishedAt).toDateString().slice(4)}</span>
      </RecentPost>
    </Link>
  ));
Enter fullscreen mode Exit fullscreen mode
  • We are mapping over the posts we fetch to create a list of links to every post (excluding the post we are currently viewing).
<BsFacebook size={"3rem"} />
Enter fullscreen mode Exit fullscreen mode
  • React Icons are styled by this size attribute so we have these on each one to increase them to 3rem.
<Body>
    <PortableText content={post.body} />
</Body>
Enter fullscreen mode Exit fullscreen mode
  • You can see our PortableText we imported being used to display the body of our post by adding it as the content.
  • It's annoying to style PortableText, so I just surround it in a styled div and apply the styling there.
<RecentPostsContainer>{postsElements}</RecentPostsContainer>
Enter fullscreen mode Exit fullscreen mode
  • And finally our postsElements variable we created being surrounded by a styled component.

Props

I came up with the idea to display all of our blog posts in the right sidebar. Before we were only fetching our current post, but now we need to fetch all of them as well.

export async function getStaticProps({ params }) {
  const query = `*[_type == "post"] {
    _id,
    title,
    publishedAt,
    'slug': slug.current,
    body
  }`;

  let posts = await client.fetch(query);

  const post = await posts.find((post) => post.slug == params.slug);

  posts.splice(
    posts.indexOf(posts.find((post) => post.slug == params.slug)),
    1
  );

  return {
    props: { posts, post },
  };
}
Enter fullscreen mode Exit fullscreen mode

Not a lot changed, but I'll explain the general idea of it.

  • We no longer fetch just the current post, we now get every single entry with a type of post.

  • After all of our posts are returned, we run a find method to match the post in our array the contains the same slug as our url params.

  • Then we splice out the post we just fetched because we don't want to display that one in our list of posts on the side.

  • Finally we return all of our posts excluding our current post, and our current post.


Preview

CodeSandbox Code -- Fullscreen Preview
Here you can view my code, there are some changes to remove fetching and the file structure is obviously incorrect. This is supposed to just be a reference to how the page and /pages/posts/[slug].js code should look for you (minus the slight changes).

That's the end of this part. We didn't alter our project too much, but we now have a great looking page to display each of our posts.

I know that was a lot of code, and there is always a possibility something goes wrong on your end. So if there is something you just cannot seem to fix, send me a message on Twitter and I will help you out as much as I can.


Next Lesson

I think I will skip styling the homepage for now. It would be a fantastic exercise to practice styled components.

At the end of the tutorial I'll have a link to this project which will include my personal styling if you are interested in that.

The next few lessons are going to be less code oriented and more involved with using Vercel, GitHub, and Sanity utilities.
Our goal is to...

  1. Push our code to GitHub.

  2. Deploy our website on Vercel.

  3. Deploy our Sanity Studio interface to Vercel.

Thank you to everyone who has read these lessons!

Top comments (0)