# How I Made a Navbar Component in Gatsby (React) with TypeScript, and Twin Macro

If you don't know what Twin Macro is, [check it out](https://github.com/ben-rogerson/twin.macro)! It's a great library that blends Tailwind CSS and Styled Component systems, like Emotion. I love using Twin because it lets me use Tailwind while separating my styling from my markup and methods. If you're someone who likes using styled-components for your React projects, let me show you how I did it. By the way, even though I'm using Gatsby, just know that Twin Macro is compatible with create-react-app and Next.js.

**Preliminaries:**
- For a beginner's look into Gatsby with TypeScript, [please check this article out](https://blog.lloydmiller.dev/learning-typescript-with-gatsby).
- If you haven't already, install the Gatsby CLI by running `$ npm install -g gatsby-cli`. 
- Create a new repository by running `$ npx gatsby new {APP_NAME}`. You could also do this by running `$ npm init gatsby`. Doing it this way will bring up some prompts that you could follow through to set your project up. Select `None` when it asks you for your preferred design system.
- Install Emotion by running `$ npm install @emotion/react @emotion/styled gatsby-plugin-emotion`.
    * Add `gatsby-plugin-emotion` to your list of plugins in ***gatsby-config.js***.
    * Add Twin as a babel preset by going to ***package.json*** and add this snippet: 

```
"babelMacros": {
    "twin": {
        "preset": "emotion"
    }
},
```
- Run `$ tsc --init --jsx react` to build the ***tsconfig.json*** file. You'll see a bunch of commented and uncommented lines.
    * Look for the following properties and uncomment them to get your file to look something like this:

```
{
  "include": ["./src/**/*"],
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "lib": ["dom", "es2017"],
    "jsx": "react",
    "strict": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "noEmit": true,
    "skipLibCheck": true
  }
}
```
- Create an empty Tailwind config file by running `$ npx tailwindcss-cli@latest init`.


**First Steps:**
- Because we're going to use the `styled` module from Twin Macro, we're going to have to make some adjustments so that the compiler doesn't scream at us. Let's add some types for React by running `$ npm install --save-dev @types/react`.
- Make a declaration file in the root called ***twin.d.ts*** and paste the following snippet in it: 

```
import 'twin.macro'
import styledImport from '@emotion/styled'
import { css as cssImport } from '@emotion/react'

declare module 'twin.macro' {
    const styled: typeof styledImport
    const css: typeof cssImport
}
```
- In your ***tsconfig.json*** file, add `"jsxImportSource": "@emotion/react",` to `"compilerOptions"`. 
    * Change `"jsx": "react"` to `"jsx": "react-jsx"`.
    * We'll then import our declaration file by changing the `include` line, like so: `"include": ["./src/**/*", "twin.d.ts"],`.


**The Work Begins:**
- Add a layout file making ***layout.tsx*** in ***src/components/*** and add this snippet to it: 

```
import React from 'react'
import { GlobalStyles } from 'twin.macro'

const Layout = ({ children, ...rest }) => (
    <div {...rest}>
        <GlobalStyles />
        {children}
    </div>
)

export default Layout
```
- Go to ***src/pages/index.js*** and rename it to ***src/pages/index.tsx***. Add this snippet:

```
import React from 'react
import Layout from '../components/layout'

const IndexPage = () => {
    return (
        <Layout>
            <p>Hello World!</p>
        </Layout>
    )
}

export default IndexPage
```
 
- Create a Navbar component by making a ***Navbar*** folder and make an ***index.tsx*** file and a ***styles.tsx*** file.
    * We'll be importing `tw` and `styled` which will help us make styled-components with Tailwind.


**The Work Continues:**
- In the ***styles.tsx*** file, add this code snippet: 

```
import tw, {styled} from 'twin.macro'

export const Nav = tw.nav`
    relative flex flex-wrap items-center justify-between px-4 py-5 
    md:px-24 lg:px-8 
`
export const InnerNav = tw.div`
    container px-4 mx-auto flex flex-wrap items-center justify-between
`
export const InnerMost = tw.div`
    w-full relative flex justify-between lg:w-auto lg:static lg:block 
    lg:justify-start
`
export const DropDown = styled.div((props: any) => [
    tw`lg:flex flex-grow items-center`,
    props.open ? tw`flex` : tw`hidden`
]) 

export const Logo = tw.a`
    leading-relaxed inline-block mr-4 whitespace-nowrap 
`
export const NavButton = tw.button`
    text-black cursor-pointer text-xl leading-none px-3 py-1 border border-solid 
    border-transparent rounded bg-transparent block lg:hidden outline-none 
    focus:outline-none
`
export const DropMenu = tw.ul`
    flex flex-col lg:flex-row list-none lg:ml-auto md:w-full md:items-center
    lg:justify-end
`
export const ListItem = tw.li`
    block
`
export const InnerItem = tw(ThisLink)`
    px-3 py-2 flex items-center font-sans font-semibold text-lg tracking-wide 
    text-gray-700 hover:text-gray-900 
`
export const ExtraButton = tw.a`
    text-black bg-transparent border border-solid border-gray-900 
    hover:bg-black hover:text-white active:bg-gray-600 font-sans font-semibold 
    uppercase text-sm px-4 py-2 rounded outline-none focus:outline-none mr-1 
    mb-1 mt-4 lg:mt-0 lg:ml-24
`
export const Bars = tw.div`
    mt-2
`
export const Bar = tw.div`
    w-8 h-1 bg-black mb-1 align-middle
`
```
- If you look at the code above, you'll see one `styled` module. This enables us to use props, which we'll need to pass our `open` data to.
    * **NOTE:** Don't pay too much to the names of these components. You can name them however you wish, as long as you can follow them and the people that will read your code can follow them.
- Now we need to get our components into ***index.tsx***. This is where our markup will be housed.
    * If you look at the code below, you'll realize that we're using `StaticImage`. This component is used when we're not loading dynamic images.

```
import React from 'react'
import {Nav, InnerNav, InnerMost, DropDown, Logo, NavButton,
    DropMenu, ListItem, InnerItem, ExtraButton, Bars, Bar
} from './styles'
import {StaticImage} from 'gatsby-plugin-image'


const Navbar = () => {

    return (
        <>
            <Nav>
                <InnerNav>
                    <InnerMost>
                        <Logo href="/">
                        <StaticImage 
                            src="../../images/logo.png"
                            alt="logo"
                            width={180}
                            placeholder="none"
                        />
                        </Logo>
                        <NavButton
                            type="button"
                        >
                            <Bars>
                                <Bar />
                                <Bar />
                                <Bar />
                            </Bars>
                        </NavButton>
                    </InnerMost>
                    <DropDown>
                        <DropMenu>
                        <ListItem>
                                <InnerItem
                                    href="/products" 
                                    aria-label="Products"
                                >
                                    Products
                                </InnerItem>
                            </ListItem>
                            <ListItem>
                                <InnerItem
                                    href="/pricing" 
                                    aria-label="Product pricing"
                                >
                                    Pricing
                                </InnerItem>
                            </ListItem>
                            <ListItem>
                                <InnerItem
                                    href="/about" 
                                    aria-label="About us"
                                >
                                    About Us
                                </InnerItem>
                            </ListItem>
                            <ListItem>
                                <InnerItem
                                    href="/faq" 
                                    aria-label="FAQs"
                                >
                                    FAQs
                                </InnerItem>
                            </ListItem>
                            <ExtraButton 
                                href="/contact-us"
                            >
                                CONTACT US
                            </ExtraButton>
                        </DropMenu>
                    </DropDown>
                </InnerNav>
            </Nav>
        </>
    )
}

export default Navbar
```
- There are a few things that we need to do now. Currently, our navbar is responsive because of our CSS (Tailwind) styles. However, if we were to click on the hamburger button, nothing will happen. Hence, we'll need to tell React to listen for this click, and we'll need to define a method to expand our menu when clicked.
    * We'll add React's `onClick` event listener which will fire off our event handler, `handleClick()`. 
    * We'll then need to need to define our state that will say whether our navbar is open or not. The `handleClick()` method will then be in charge of facilitating our state change.
    * Finally, we'll pass our state to the extended component in our styles that has conditional styles depending on whether our navbar is open or not. We'll do this with a prop called `open`. Our markup will now look like this:

```
import React, {useState} from 'react'
import {Nav, InnerNav, InnerMost, DropDown, Logo, NavButton,
    DropMenu, ListItem, InnerItem, ExtraButton, Bars, Bar
} from './styles'
import {StaticImage} from 'gatsby-plugin-image'


const Navbar = () => {
    const [navbarOpen, setNavbarOpen] = useState(false)

    function handleClick(){
        setNavbarOpen(!navbarOpen)
    }

    return (
        <>
            <Nav>
                <InnerNav>
                    <InnerMost>
                        <Logo href="/">
                        <StaticImage 
                            src="../../images/logo.png"
                            alt="logo"
                            width={180}
                            placeholder="none"
                        />
                        </Logo>
                        <NavButton
                            type="button"
                            onClick={handleClick}
                        >
                            <Bars>
                                <Bar />
                                <Bar />
                                <Bar />
                            </Bars>
                        </NavButton>
                    </InnerMost>
                    <DropDown open={navbarOpen}>
                        <DropMenu>
                        <ListItem>
                                <InnerItem
                                    href="/products" 
                                    aria-label="Products"
                                >
                                    Products
                                </InnerItem>
                            </ListItem>
                            <ListItem>
                                <InnerItem
                                    href="/pricing" 
                                    aria-label="Product pricing"
                                >
                                    Pricing
                                </InnerItem>
                            </ListItem>
                            <ListItem>
                                <InnerItem
                                    href="/about" 
                                    aria-label="About us"
                                >
                                    About Us
                                </InnerItem>
                            </ListItem>
                            <ListItem>
                                <InnerItem
                                    href="/faq" 
                                    aria-label="FAQs"
                                >
                                    FAQs
                                </InnerItem>
                            </ListItem>
                            <ExtraButton 
                                href="/contact-us"
                            >
                                CONTACT US
                            </ExtraButton>
                        </DropMenu>
                    </DropDown>
                </InnerNav>
            </Nav>
        </>
    )
}

export default Navbar
```

**The Work Ends:**
- Let's go back to our ***styles.tsx*** file. This is where we'll get deeper into TypeScript. In React, we have the `FC` (`Function Component`) generic type which can be used on arrow functions to define and extend arrow function components. `FC` allows us to pass props into our component and helps us define default types for it as well. It also includes the `children` prop type by default, but we will be explicitly defining this in our component.
    * Next, we make a type called `ButtonProps` where we'll define the props that we know will be passed to our button component (type, children, onClick). For `onClick`, we'll define it with a function that uses React's default `MouseEvent` interface. TypeScript pros have a saying that you should `never` use the `any` type, but for brevity, we'll just give `children` an `any` type.
    * With the help of `FC`, we can then pass in our defined props to define our extended component. 

```
import React, {FC, MouseEvent} from 'react'
import tw, {styled} from 'twin.macro'

type ButtonProps = {
    type: string,
    children?: any,
    onClick?: (event?: MouseEvent<HTMLButtonElement>) => void,
}

const ThisButton: FC<ButtonProps> = ({onClick, type, children, ...rest}) => 
    <button onClick={onClick} {...rest}>{children}</button>

...

export const NavButton = tw(ThisButton)`
    text-black cursor-pointer text-xl leading-none px-3 py-1 border border-solid 
    border-transparent rounded bg-transparent block lg:hidden outline-none 
    focus:outline-none
`
```


- Now in your ***layout.tsx*** file, import the `Navbar` component and place it right below the `GlobalStyles` component.

```
import React from 'react'
import { GlobalStyles } from 'twin.macro'
import Navbar from './Navbar'

const Layout = ({ children, ...rest }) => (
    <div {...rest}>
        <GlobalStyles />
        <Navbar />
        {children}
    </div>
)

export default Layout
```

**Conclusion:**
- If you try it out, you will notice that clicking on one of the links in the menu will bring you to a new page with the menu still open. To remedy this, in our ***Navbar/index.tsx*** file, we could pass an `onClick` event listener to each link component, which in our case is `<InnerItem />`.
    * If we need to, we can define our links like so:

```
import React, {FC, MouseEvent} from 'react'
import tw, {styled} from 'twin.macro'

type LinkProps = {
    href: string,
    "aria-label"?: string,
    onClick?: (event?: MouseEvent<HTMLAnchorElement>) => void,
}

const ThisLink: FC<LinkProps> = props => <a {...props} />
...

export const InnerItem = tw(ThisLink)`
    px-3 py-2 flex items-center font-sans font-semibold text-lg tracking-wide 
    text-gray-700 hover:text-gray-900 
`
```
- Don't feel distressed if you don't get these concepts right away. It just takes some practice, which to be honest. There are so many things that I still need to learn about TypeScript, and so far I'm liking the journey.
