Styling is an essential part of building web and mobile applications. For an application to look presentable to your users, it needs to be styled. CSS is what we most commonly use to beautify our applications.
But as much as we love CSS, there are annoying issues to deal with, such as class name duplication, misspelled and unknown class names, uncertainty about whether class names will fit, and many more. In projects where you have a large codebase, using plain CSS can become time-intensive and tiring. That’s where styled-components comes in.
In this tutorial, we’ll show you how to build and style a TypeScript app using styled-components.
To demonstrate with a practical example, we’ll use styled-components and TypeScript to create an Amazon-like e-commerce page. The finished product will look something like this:
You can access the images required for this demo in my GitHub repository. By the end of this tutorial, you should be able to style your own TypeScript projects using styled-components.
Styled-components is a CSS-in-JS library that enables you to write regular CSS and attach it to JavaScript components. With styled-components, you can use the CSS you’re already familiar with instead of having to learn a new styling structure.
styled-components lets you style your work without having to worry about whether class names clash or whether they’re appropriate for a given use case. It also helps with debugging because once you figure out what component has an issue, you know where to find its styles.
We’ll use create-react-app
to generate a template for our project. Since it’s a TypeScript project, we’ll add --template typescript
to our command.
Open your terminal and type the following, then press enter:
npx create-react-app --template typescript
After creating your project, go into the project directory by typing the following:
cd amazon-clone
Type code .
in your terminal to open the project in your code editor. Alternatively, you can open it from your file manager by right-clicking the folder and selecting Open with code.
We need to install some dependencies because styled-components doesn’t come with create-react-app.
Run the following command:
yarn add styled-components
When it’s done installing, run the following:
yarn add -D @types/styled-components
This installs the styled-components types for TypeScript as a dev dependency.
We’re also going to use Material Icons, so let’s install the material-ui package
. Type the following and press enter:
yarn add @material-ui/icons @material-ui/core
There’s a styled-components extension for VS Code that makes it look like we’re typing actual CSS even though it’s a TypeScript file. It’s called vs-code-styled-components
and I suggest you install it before proceeding if you haven’t already.
Your folder should now look this:
Now it’s time to clean up our project. We do not need all the files generated by Create React App, so let’s delete the superfluous files:
index.html
file, change the title to whatever you want and delete all the information from the meta description to where the title tag startssrc
folder, delete all files except the App.tsx
and index.tsx
index.tsx
, delete the lines importing index.css
and reportWebVitals
. Also remove all lines related to webVitalsApp.tsx
, except where we import React and export the appWith that out of the way, let’s start building.
First, let’s plan the layout of our page:
Our page is made up of three main parts: the navbar, the menu bar, and the body. We will start from top to bottom.
In App.tsx
, create a React functional component. It should look like this:
import React from 'react'; const App:React.FC = ()=> ( <> </> ) export default App;
In your src
folder, create a styles folder. You can name it whatever you like; I’ll name mine styles
.
In the styles folder, create a file and name it global.ts
. This file will contain our global styles. Global styles are styles that are used by all elements in a page.
In global.ts
, we import createGlobalStyle
and create the global styles. Your global.ts
file should look like this:
import {createGlobalStyle} from "styled-components" export default createGlobalStyle` *{ margin: 0; padding: 0; outline:0; box-sizing:border-box; font-family: 'Open Sans', sans-serif; } #root{ margin:0 auto; } `
These styles ensure that there is no margin or padding on the page, except in places where they are explicitly stated.
In the src
folder, we’ll create a folder for the components. In the components folder, we’ll create three more folders, one for each section of the page. We’ll create one more folder for our assets. All the images should be stored in this folder.
In the navbar folder, create two files: index.tsx
and styles.tsx
. In index.tsx
, import the following items:
import React from 'react' import { Container, Logo, Flag, Text, Wrapper, Searchbox, Select, SearchIconWrapper } from "./styles" import { ShoppingCartOutlined, SearchOutlined, ArrowDropDown, RoomOutlined } from '@material-ui/icons'; import logo from "../assets/logo.png" import flag from "../assets/flag.png"
Importing React is important because we need it to create our function.
On the second line, we imported a number of components from the styles file (we’ll see what they do soon). Then, we imported our icons from Material Icons. Finally, we imported our images from the assets folder.
In the index folder, let’s create a functional component and put our container component in it:
import React from 'react'; const App:React.FC = ()=> ( <> <Container></Container> </> ) export default App;
This may look strange if you’re not used to using styled-components.
The container component represents the overall wrapper for the other components that we’ll use. It’s used in the same way as a div
when styled-components is not used. When we start with the styling, you’ll come to learn that the container component is really a div
under the hood.
Let’s see what the style file looks like:
import styled from "styled-components" export const Container = styled.div` display: flex; justify-content:space-evenly; align-items: center; color: white; background-color: #131A22; `
At the top, we imported styled
from styled-components
. styled
gives us the option to use any HTML element, such as div
, section
, span
, inputs
, etc. We have access to them all.
For our container component, we used a div
. After the element’s name, we added template literals
, which serves as the wrapper for the component’s styles. We then wrote our CSS styles inside the template literals.
Notice how it looks like I’m writing plain CSS? That’s because of the extension I mentioned earlier. It helps with the colors and autocompletion.
These components are reusable and they make it easier to associate each component with its style. This is because each component already has its style in its body, so when you need to debug, you immediately know where to check.
Let’s create one more folder: a helper folder. In the helper folder, we’ll create two files: PageText.tsx
and ItemWrapper.tsx
.
The page has a lot of text on it with different sizes and colours. To avoid repetition, we need to create TypeScript components and pass some CSS properties as props. This will enable us to reuse the components and modify them as we want without the need to write new lines of code in our styles file.
The PageText
component will be for all the text on the page. The ItemWrapper
component will be used as a container to wrap various elements on the page.
In PageText.tsx
, insert the following:
import React from 'react' interface Props{ className?: string; fontSize?: any; color?: string; } export const PageText:React.FC <Props> = ({className, children}) => { return ( <span className={className}>{children}</span> ) }
Notice the prop being passed in the component? These are the props that will be accepted when using the component. If you don’t include the className
prop, whatever style you try to declare in the component body will not take effect. When working with it, you can remove the className
props to see what error it throws. The children props represent any item that we will write or use inside the component.
In ItemWrapper.tsx
, insert the following lines of code:
import React from 'react' interface Props{ className?: string; display?: string; flexDirection?: string; alignItems?: string; maxWidth?: string; margin?: string; } export const PageItemWrapper:React.FC <Props> = ({className, children}) => { return ( <div className={className}>{children}</div> ) }
Now that we’re finished with the helpers, let’s create our navbar. The navbar of the example page starts with the Amazon logo. We already imported it in our index file, so let’s create a component for it and style it as well:
import{ PageText as NavText} from "../helpers/PageText" import{ PageItemWrapper as NavItemWrapper} from "../helpers/ItemWrapper" export const Logo = styled.img` width: 6em; border:1px solid #131A22; padding: .2em .1em; cursor:pointer; &:hover{ border:1px solid #ffffff; border-radius: .2em; } `
We created styles for the width, border, padding, and cursor behavior. We also added additional styles for when the logo is hovered on. Unlike in plain CSS, where we’d use something like a:hover
, here we use the &
symbol to signify that we’re creating styles for when the element is hovered on.
This same process is used when creating styles for ::before
, ::after
, and other pseudo-elements. One of the best things about styled-components is that you can write all of your styles in one place.
Now that we’re done with the logo, let’s move on to the other parts of the navbar.
In styles.tsx
, add the following:
import{ PageText as NavText} from "../helpers/PageText" export const Text = styled(NavText)` color:${(props)=>props.color ? props.color :"#ffffff" }; font-size: ${(props)=>props.fontSize ? props.fontSize : ".9em" }; `
Here, we used styled-components in another way. We imported PageText
as NavText
from the helper folder. In place of the element’s name, we used NavText
.
Note that when using an existing component, we didn’t include a dot. Instead of writing styled.NavText
, we wrote styled(NavText)
. We also created default styles for our component.
The lines of code inside the component mean that if a color
or font-size
prop is specified when using the component, then color
and font-size
are equal to the specified one, Otherwise, the text component uses the default values stated therein.
import{ PageItemWrapper as NavItemWrapper} from "../helpers/ItemWrapper" export const Wrapper = styled(NavItemWrapper)` display: flex; flex-direction: ${(props)=>props.flexDirection ? props.flexDirection : "column"}; align-items: ${(props)=>props.alignItems ? props.alignItems : "flex-start"}; padding: .1em; cursor:pointer; border:1px solid #131A22; &:hover{ border:1px solid #ffffff; border-radius: .2em; } @media(max-width:850px){ display: none; } `
Now let’s use them in our page. In index.tsx
, add the following lines of code:
const Navbar:React.FC = () => { return ( <> <Container> <Logo src={logo}/> <Wrapper flexDirection="row" alignItems="center"> <RoomOutlined/> <Wrapper > <Text fontSize=".7em">Deliver in </Text> <Text >Nigeria</Text> </Wrapper> </Wrapper> {/* the search button */} <Select> <option value="All">All</option> </Select> <Searchbox/> <SearchIconWrapper> <SearchOutlined/> </SearchIconWrapper> {/* flag image */} <Wrapper flexDirection="row" alignItems="flex-start" > <Flag src={flag}/> <ArrowDropDown/> </Wrapper> <Wrapper> <Text fontSize=".7em">Hello, Sign in</Text> <Wrapper flexDirection="row" alignItems="center"> <Text>Account & Lists </Text> <ArrowDropDown/> </Wrapper> </Wrapper> <Wrapper> <Text fontSize=".7em">Returns</Text> <Text >& Orders</Text> </Wrapper> <Wrapper flexDirection="row" alignItems="center"> <Wrapper alignItems="center"> <Text color="#ff9900">0</Text> <ShoppingCartOutlined/> </Wrapper> <Text>Cart</Text> </Wrapper> </Container> </> ) } export default Navbar
It’s a lot of code, but don’t get overwhelmed. Let’s go through it together.
We used our Wrapper
and NavText
components, and you can see how to modify them as needed. We also used the icons that we imported at the beginning of this tutorial. The only new things here are the Select
, Searchbox
, SearchIconWrapper
, and Flag
components. Let’s explain what they are and how we created them.
In the page’s navbar, there is a search box and two elements on both sides of it. Let’s create them.
In styles.tsx
:
export const Searchbox = styled.input` background-color: #ffffff; padding: .78em; width: 47%; border: none; @media(max-width:850px){ border-radius: .2em; margin: .3em 0; } ` export const Select = styled.select` background-color:#ddd; margin-right: -1.2em; padding: .72em .5em; border-radius: .2em 0em 0em .2em; border: none; cursor: pointer; @media(max-width:850px){ display: none; } ` export const SearchIconWrapper = styled.span` background-color:#fabd60; color: #131A22; margin-left: -1em; border-radius: 0em .2em .2em 0em ; padding: .32em .5em; cursor: pointer; transition: all 250ms ease; &:hover{ background-color:#ff9900; } @media(max-width:850px){ display: none; } ` export const Flag = styled.img` width:2em; `
We used an input
tag for the Searchbox
component, a select
tag for the Select
component, an img
tag for the Flag
component, and a span
for the SearchIconWrapper
. We also added a hover effect for the SearchIconWrapper
as well as media queries for the new components.
That’s all for the navbar. Now your page should look like this:
Now, let’s build the menu bar.
In the Menubar
folder that we previously created, create an index.tsx
file and a styles.tsx
file.
In index.tsx
, create a functional component and add the following line in it:
import React from 'react' import {Container, Wrapper, Text, LeftText} from "./styles" import {Menu} from '@material-ui/icons'; const Menubar:React.FC = () => { return ( <></> ) } export default Menubar
We imported some components from styles.tsx
and the menu icon from Material Icons. Just like in our Navbar
folder, we’ll import the wrapper and text components from the helper folder and use it in our styles.tsx
file:
import styled from "styled-components" import{ PageText as MenuText} from "../helpers/PageText" import{ PageItemWrapper as MenuItemWrapper} from "../helpers/ItemWrapper" export const Container = styled.div` display: flex; justify-content:space-between; color: white; padding: .3em; background-color: #232f3e; ` export const Text = styled(MenuText)` color:${(props)=>props.color ? props.color :"#ffffff" }; font-size: ${(props)=>props.fontSize ? props.fontSize : ".9em"}; margin-right: 1em; border:1px solid #232f3e; padding: .5em .1em; cursor:pointer; &:hover{ border:1px solid #ffffff; border-radius: .2em; } @media(max-width:850px){ display: none; } ` export const LeftText = styled(Text)` @media(max-width:850px){ display: block; } ` export const Wrapper = styled(MenuItemWrapper)` display: flex; flex-direction: row; align-items: center; margin-right: 1em; `
We styled our Container
, Wrapper
, and Text
components. We also made the color and font size of the Text
component customizable. Let’s use them in our index.tsx
file:
const Menubar:React.FC = () => { return ( <Container> <Wrapper> <Wrapper> <Menu/> <Text>All</Text> </Wrapper> <Wrapper> <Text>Today's Deals</Text> <Text>Customer Service</Text> <Text>Gift Cards</Text> <Text>Sell</Text> <Text>Registry</Text> </Wrapper> </Wrapper> <Wrapper> <LeftText>Amazon's response to COVID-19</LeftText> </Wrapper> </Container> ) } export default Menubar
Our menu bar should look like this:
Now let’s build our page body.
On the left side of the page, there is a dropdown showing various department names. We also have product details on the page. Let’s create a new file in our PageBody
folder to store this information.
In PageInfo.ts
:
import battery from "../assets/cell-battery.jpg" import headset from "../assets/headset.jpg" import screen from "../assets/screen-protector.jpg" export const departmentList = [ "Electronics", "Cell Phones & Accessories", "Industrial & Scientific", " Office Products", " Video Games", " Health, Household & Baby Care", " Automotive Parts & Accessories", " Clothing, Shoes & Jewelry", "Sports & Outdoors", " Tools & Home Improvement", "Home & Kitchen", "Musical Instruments", "Pet Supplies", "Appliances", "Arts, Crafts & Sewing", " Toys & Games", " CDs & Vinyl" ] export const productDetails=[ { name:"Amazon Basics 4 Pack CR2032 3 Volt Lithium Coin Cell Battery", by: "by Amazon Basics", starcount: "59,531", price: "", shipping: "", available: "Currently unavailable.", src: battery }, { name: "Arae Screen Protector for iPhone 12 Pro Max, HD Tempered Glass Anti Scratch Work with Most Case, 6.7 inch, 3 Pack", by: "by Arae", starcount: "321", price: "$6.99", shipping: " + $17.27 shipping", available: "Arrives: Thursday, May 20", src: screen }, { name: "SENZER SG500 Surround Sound Pro Gaming Headset with Noise Cancelling Microphone - Detachable Memory Foam Ear Pads - Portable Foldable", by: "by SENZER", starcount: "1,613", price: "$23.99 ", shipping: " + $23.39 shipping", available: "Arrives: Thursday, May 20", src: headset } ]
In the PageInfo
file, we have departmentList
, which is an array of the department names, and productDetails
, an array of objects containing product information. At the top, we imported the product images from the assets folder.
Let’s create some new files in the PageBody
folder. Create an index.tsx
file and a styles.tsx
file. In index.tsx
, add the following:
import React from 'react' import { Container, LeftContainer, RightContainer, Wrapper, Image, Text, Paragraph, ProductContainer, ImageContainer, SearchResultDiv, IconWrapper, BoldText } from "./styles" import {KeyboardArrowLeft, Star, StarHalf, KeyboardArrowDown} from '@material-ui/icons'; import {departmentList, productDetails} from "./PageInfo"
We imported our components from the styles file and our icons and the page’s information from PageInfo.ts
.
Now let’s create our components and style them.
In your styles.tsx
file, add the following lines of code:
import styled from "styled-components" import {PageText} from "./PageText" import {PageItemWrapper} from "./PageItemWrapper" export const Container = styled.div` display: flex; padding: 1em; ` export const LeftContainer = styled.aside` height:80vh; width: 18vw; border-right: 2px solid #ddd; @media(max-width:650px){ display: none } ` export const RightContainer = styled.section` height:80vh; width: 82vw; display: flex; flex-direction: column; margin-left: 1.5em; ` export const Image = styled.img` width: 13em; ` export const Text = styled(PageText)` color:${(props)=>props.color ? props.color :"#131A22" }; font-size:${(props)=>props.fontSize ? props.fontSize :".9em" }; ` export const BoldText = styled(Text)` font-weight: bold; padding: .4em; ` export const Paragraph = styled.p` font-size:.9em; display: flex; align-items: center; padding-bottom: .1em; ` export const SearchResultDiv = styled.div` border: 1px solid #ddd; padding: .6em; width: 95%; border-radius: 4px; ` export const ProductContainer = styled.div` display: grid; grid-template-columns: repeat(auto-fit, 22em); margin-top: 2em; @media(max-width:915px){ grid-template-columns: repeat(auto-fit, 15em); align-items: center; justify-content: center; } ` export const ImageContainer = styled.div` height: 14em; display: flex; align-items: center; ` export const Wrapper = styled(PageItemWrapper)` display: flex; margin-right: 1em; flex-direction: ${(props)=>props.flexDirection ? props.flexDirection : "row"}; align-items: ${(props)=>props.alignItems ? props.alignItems : "left"}; margin: ${(props)=>props.margin ? props.margin : ""}; ` export const IconWrapper = styled.div` color: #ff9900; `
In the code above, we created a container for our page body, then divided the page into two sections. The left section contains the department list and the right section contains the products and their information.
We then created a component for our product images and also used our imported helper components.
If you take a look at the BoldText
component, you’ll notice that it’s an extension of the PageText
component with some new styles in it. We use it to create texts that are bolder than the page’s text. With styled-components, you can extend another component. It’s like copying the component with all of its styles and then adding extra styles to it.
The SearchResultDiv
component is the long bar above the product images that contains the search result details.
We also created a ProductContainer
component that contains our products and their information, then an image container for the product image.
The last component in our styles file is a wrapper for our star icons.
If you’re so inclined, you can take some time to study the styles and understand what each line does if you feel like it.
Now, let’s use the components in our index file:
const Menubar:React.FC = () => { return ( <Container> <LeftContainer> <BoldText>Department</BoldText > {departmentList.map(item =>( <Paragraph> <KeyboardArrowLeft/>{item}</Paragraph> ))} </LeftContainer> <RightContainer> <SearchResultDiv> <BoldText>1-12 of over 2,000 results for </BoldText> <BoldText color="#c45500"> All Departments </BoldText> </SearchResultDiv> <ProductContainer> {productDetails.map(item =>( <Wrapper flexDirection="column" > <ImageContainer> <Image src={item.src}/> </ImageContainer> <Text>{item.name}</Text> <Text fontSize=".8em" color="grey" >{item.by}</Text> {/* stars */} <Wrapper margin=".3em 0 0 0" alignItems="center" > <IconWrapper> <Star/> <Star/> <Star/> <Star/> <StarHalf/> </IconWrapper> <KeyboardArrowDown/> <Text color="blue">{item.starcount}</Text> </Wrapper> <Wrapper alignItems="center" margin=".3em 0 .3em 0" > <Text fontSize="1.3em">{item.price }</Text> <Text color="grey">{ item.shipping}</Text> </Wrapper> <Text>{item.available}</Text> </Wrapper> ))} </ProductContainer> </RightContainer> </Container> ) } export default Menubar
Above, we used the components that we created to build our page body and customized them to fit wherever they are supposed to be.
We made use of our product details, which were imported from the pageInfo
file. In the LeftContainer
component, we mapped through the departmentList
array. And in the ProductContainer
component, we mapped through the productDetails
array. Mapping through the array instead of writing the details one after another enables us to write cleaner, less repetitive code.
That’s all for the page’s body. Our page should look like this now:
Congratulations! You now have all the foundational knowledge and skills to style your own TypeScript projects with styled-components. You can check out the finished example page here.
To learn more about styled-components, you can check out the official documentation.
LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowReact Native’s New Architecture offers significant performance advantages. In this article, you’ll explore synchronous and asynchronous rendering in React Native through practical use cases.
Build scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
5 Replies to "Using styled-components in TypeScript: A tutorial with examples"
This is a great tutorial, can you please make this tutorial better by specifying file to copy these codes? Thank you.
Hi, thank you for reading. If you’re looking for a repository where you can find the code used in this tutorial, there’s a link in the introduction: https://github.com/Iamhafsah/amazon-page/tree/main/src/components/assets
Hope that helps!
Hi there! Great tutorial! This is my first crack at typescript and this made it very easy to understand. I’m in the middle of this and I just have some comments as I’m going through. The jumping back and forth and naming of folders made it a bit difficult to follow. For example the create react app doesn’t specify that’s where you should also put the name of your folder. If you’re expericenced with react you can figure it out but I did create react app and was wondering where the amazon clone folder came from because I should have read ahead and figured out that should have been when I created the react app in the first place.
And then there was a part saying that there are three parts to what we are creating, the nav bar, the body, and the menubar. While we were inside of the components folder this tutorial tells us to go to the index folder in which I think it meant the App.tsx file. But I was re-reading trying to find if I missed where this index folder should have gone. Was I supposed to make an index file inside of the components folder? Where the three folders I made correct? Looking at the code I figured it was talking about the App.tsx file.
The next step after that specifies to go to our style file but at that point we have two style files. It’s a container that, I’m guessing we are in the global folder? It’s hard to tell where the container goes. The example doesn’t show the global code that we entered but if we are using the container style in app.tsx and in the navbar index.tsx then maybe it should be in the global? I don’t know just following this tutorial is a bit confusing and I think there should be some breadcrumbs as far as describing what is going where.
But besides the confusion I’m really enjoying this tutorial and I love styled components. I think one of the most valueable things I took away from this tutorial is that vs styled components extension! OMG I’ve just been writting my styles with styled components by hand. I also love seeing the file organization.
so where did you specify ts types? I didn’t find even one
create-react-app–template typescript