Categories
Next.js

Next.js — CSS-in-JS, Fast Refresh, and Environment Variables

We can create server-side rendered React apps and static sites easily Next.js.

In this article, we’ll take a look at how to style pages, use fast refresh, and add environment variables with Next.js.

Less and Stylus Support

Next.js support Less and Stylus for styling.

To add support for it, we install the @zeit/next-less and @zeit/next-stylus plugins.

CSS-in-JS

We can also use any CSS-in-JS solution for styling our Next.js app.

For example, we can add inline styles:

function Hello() {
  return <p style={{ color: 'blue' }}>hello world</p>
}

export default Hello

We can also use styled-jsx to put CSS in our JavaScript files.

It comes with Next.js.

For example, we can write:

function Hello() {
  return <div>
    <style jsx>{`
        p {
          color: blue;
        }
        div {
          background: red;
        }
        @media (max-width: 600px) {
          div {
            background: blue;
          }
        }
      `}</style>
    <style global jsx>{`
        body {
          background: lightgray;
        }
      `}</style>
    <p>hello world</p>
  </div>
}

export default Hello

We add style tags which contains local and global styles.

Global styles have the global prop and local ones don’t.

Fast Refresh

Fast refresh is a Next.js feature that makes development easier.

It gives us instant feedback on edits made to our React components.

It’s enabled by default on Next 9.4 or later.

This feature lets make edits visible without losing component state.

It’ll reload the component if we edit a component.

If we edit something that that’s not a component but it’s used by them, then both files will be refreshed.

If a file that’s imported by files outside of the refresh tree, then fast refresh will do a full reload.

When any runtime errors are encountered, then we’ll see an overlay with the error displayed.

If we added error boundaries to our app, then they’ll retry rendering on the next edit.

If there’re any hooks, then their state will be updated when a fast refresh happens.

Static File Serving

Next.js apps can have static files.

We just have to put them inside the public folder.

For example, we can write:

function Hello() {
  return <div>
    <img src="/kitten.jpg" alt="kitten" />
  </div>
}

export default Hello

given that kitten.jpg is in the public folder.

We’ll see the kitten picture displayed.

We shouldn’t change the name of the public folder since it’s the only folder that can be used to serve static assets.

Environment Variables

We can load environment variables from an .env.local file.

For example, we can create an .env.local file by writing:

API_KEY=...

And then we can create a page that uses the API_KEY environment variable with:

function Photos({ data }) {
  return <p>{JSON.stringify(data)}</p>
}

export async function getServerSideProps() {
  const headers = new Headers();
  headers.append('Authorization', process.env.API_KEY);
  const res = await fetch(`https://api.pexels.com/v1/search?query=people`, {
    method: 'GET',
    headers,
    mode: 'cors',
    cache: 'default',
  })
  const data = await res.json()
  return { props: { data } }
}

export default Photos

We access our environment variable with the process.env object.

These environment variables are only available in the Node.js environment.

Conclusion

Next.js 9.4 or later comes with the fast refresh feature to make development more convenient.

Also, Next.js comes with CSS-in-JS support.

We can also use environment variables in our apps.

Categories
Next.js

Next.js — Client-Side Navigation and API Routes

We can create server-side rendered React apps and static sites easily Next.js.

In this article, we’ll take a look at routing with Next.js.

Client-Side Navigation

In addition to routing on server-side, we can also do client-side navigation with Next.js.

For example, we can write:

pages/links.js

import { useRouter } from 'next/router'

function Links() {
  const router = useRouter()

  return (
    <ul>
      <li>
        <span onClick={() => router.push('/foo')}>foo</span>
      </li>
      <li>
        <span onClick={() => router.push('/bar')}>bar</span>
      </li>
    </ul>
  )
}

export default Links

pages/foo.js

import Links from './links';

function Foo() {
  return <div>
    <Links />
    <p>foo</p>
  </div>
}

export default Foo

pages/bar.js

import Links from './links';

function Bar() {
  return <div>
    <Links />
    <p>bar</p>
  </div>
}
export default Bar

We have the pages/links.js file with spans that runs the router.push method on click.

The router object comes from Next.js’s useRouter hook.

Shallow Routing

Shallow routing lets us change the URL without running data fetching methods like getServerSideProps , getStaticProps , and getInitialProps again.

We get the updated pathname and query via the router object.

For example, we can write:

pages/count.js

import { useEffect } from 'react'
import { useRouter } from 'next/router'

function Count() {
  const router = useRouter()

  useEffect(() => {
    router.push('/count?count=10', undefined, { shallow: true })
  }, [])

  useEffect(() => {
  }, [router.query.count])

  return <p>{router.query.count}</p>
}

export default Count

to add a Count component with a useEffect hook that calls router.push to add a query string to the URL.

shallow: true means that we enable shallow routing.

We should see 10 displayed since router.query.count is 10 as we see from the query string when we go to http://localhost:3000/count.

Shallow routing only works for same page URL changes.

So if we have:

router.push('/?count=10', '/about?count=10', { shallow: true })

then the about page would be loaded from scratch.

API Routes

We can use API routes to build an API with Next.js.

To do that , we can put JavaScrtipt files inside the pages/api folder.

Then the files inside would be mapped to the /api/* URLs.

For example, we can write:

pages/api/hello.js

export default (req, res) => {
  res.statusCode = 200
  res.json({ name: 'hello world' })
}

req has the requests data and res is an object we can use to create our response.

We set the response status code with res.statusCode and we create our JSON response with the res.json method.

Now when we make a request to http://localhost:3000/api/hello with any HTTP verb, we’ll see:

{
  "name": "hello world"
}

returned.

To handle different HTTP methods, in an API route, we can use the req.method property.

For example, we can write:

export default (req, res) => {
  res.statusCode = 200
  res.json({ name: 'hello world', method: req.method })
}

Now when we make a PUT request to http://localhost:3000/api/hello, we get:

{
    "name": "hello world",
    "method": "PUT"
}

returned.

API routes don’t specify CORS headers, so we can only make these requests if the request originates from the same origin as the API route.

Conclusion

We can make API routes with Next.js.

Navigation can be done from the client side with the useRouter hook.

Categories
Next.js

Getting Started with Next.js

We can create server-side rendered React apps and static sites easily Next.js.

In this article, we’ll take a look at how to get started with Next.js.

Getting Started

We can create our Next.js project with its provided command-line program.

To create the project, we just run:

npx create-next-app

or:

yarn create next-app

to create our project.

Then to start the dev server, we run:

npm run dev

Then we go to http://localhost:3000/ and our app.

Now we can create a component in the pages folder to add our page.

We go into the pages folder, create an hello.js file and add:

function Hello() {
  return <div>hello world</div>
}

export default Hello

to it.

We’ve to remember to export the component so that it’ll be rendered.

Then we can go to http://localhost:3000/hello and see our page.

Next.js does routing automatically by the file name so we don’t have to worry about that.

Pages with Dynamic Routes

If we want to create pages with dynamic routes, we just put them in the folder structure and the URLs will follow the same structure.

For example, we can create a posts folder and create our files.

We create 1.js in pages/posts and write:

function Hello() {
  return <div>hello 1</div>
}

export default Hello

Likewise, we create 2.js in the same folder and write:

function Hello() {
  return <div>hello 2</div>
}

export default Hello

Then when we go to http://localhost:3000/posts/1 and http://localhost:3000/posts/2, we’ll see hello 1 and hello 2 respectively.

Pre-rendering

Next.js pre-renders every page by default.

The HTML for each page are created in advanced.

This is better for performance and SEO than rendering it on the client-side.

The pages only come with the JavaScript it needs to load so that it loads faster.

There’re 2 forms of pre-rendering.

One is static generation and the other is server-side rendering.

Static generation means that HTML is generated at build time and are reused on each request.

Since it reuses the pages, it’ll be the fastest way to render pages, so this is the recommended option.

The other option is server-side rendering, which renders the HTML no each request.

We can choose between the 2 types of rendering.

Static Generation with Data

We can generate pages statically with data.

To do this, we can write:

pages/yesno.js

function YesNo({ data }) {
  return <p>{data.answer}</p>
}

export async function getStaticProps() {
  const res = await fetch('https://yesno.wtf/api')
  const data = await res.json()
  return {
    props: {
      data,
    },
  }
}

export default YesNo

We created a new component file with the YesNo component.

The getStaticProps function gets the data asynchronously from an API.

Then we return the resolved value by return an object with the data in the props property.

Then we can get that data from the props in the YesNo component.

And in the JSX, we displayed the data.

Conclusion

We can create pages with Next.js that displays static content or content from an API easily.

Categories
React

How to Add Geolocation to a React App

Many apps want to get data based on location. This is where the HTML Geolocation API comes in. You can use it easily to get the location of the current device via the Internet.

To get the location of the device the browser is running with plain JavaScript, we write:

if (navigator.geolocation) {
  navigator.geolocation.getCurrentPosition(getPosition);
}

function getPosition(position) {
  console.log(position.coords.latitude, position.coords.longitude);
}

As you can see, getting the latitude and longitude is very easy. We can also easily add geolocation to any React app. The react-geolocated package is a great add-on for adding geolocation capabilities to your app. It is located at https://www.npmjs.com/package/react-geolocated.

It provides a promise-based API for getting the location of the device, so we can easily use async and await to get the location with this package.

In this article, we will build an app to weather app that lets you get the current weather and forecast of the current location you’re in. It also lets you search the weather by inputting the location manually.

To start we will run Create React App to create the React project. Run:

npx create-react-app weather-app

to create the project.

Next, we have to add some libraries. We need Axios for making HTTP requests, Formik and Yup for form value handling and validation, MobX for state management, Bootstrap for styling, React Geolocated for getting geolocation data, and React Router for routing. We install them by running:

npm i axios formik mobx mobx-react react-bootstrap react-geolocated react-router-dom yup

With all the packages installed, we can start building the app. We start by replacing the code in App.css :

.page {
  padding: 20px;
}

to add some padding to our page.

Next, replace the code in App.js with:

import React from "react";
import { Router, Route } from "react-router-dom";
import HomePage from "./HomePage";
import { createBrowserHistory as createHistory } from "history";
import Navbar from "react-bootstrap/Navbar";
import Nav from "react-bootstrap/Nav";
import "./App.css";
const history = createHistory();

function App({ keywordStore }) {
  return (
    <div className="App">
      <Router history={history}>
        <Navbar bg="primary" expand="lg" variant="dark">
          <Navbar.Brand href="#home">Geolocation Weather App</Navbar.Brand>
          <Navbar.Toggle aria-controls="basic-navbar-nav" />
          <Navbar.Collapse id="basic-navbar-nav">
            <Nav className="mr-auto">
              <Nav.Link href="/" active>
                Home
              </Nav.Link>
            </Nav>
          </Navbar.Collapse>
        </Navbar>
        <Route
          path="/"
          exact
          component={props => (
            <HomePage {...props} keywordStore={keywordStore} />
          )}
        />
      </Router>
    </div>
  );
}

export default App;

Here we add the React Bootstrap navigation bar and the route to our home page.

Next, we create a component to display the current weather. Create a file called CurrentWeather.js in the src folder and add:

import React from "react";
import { observer } from "mobx-react";
import { searchWeather } from "./request";
import ListGroup from "react-bootstrap/ListGroup";

function CurrentWeather({ keywordStore }) {
  const [weather, setWeather] = React.useState({});

  const getWeatherForecast = async keyword => {
    const response = await searchWeather(keyword);
    setWeather(response.data);
  };

  React.useEffect(() => {
    keywordStore.keyword && getWeatherForecast(keywordStore.keyword);
  }, [keywordStore.keyword]);

  return (
    <div>
      {weather.main ? (
        <ListGroup>
          <ListGroup.Item>
            Current Temparature: {weather.main.temp - 273.15} C
          </ListGroup.Item>
          <ListGroup.Item>
            High: {weather.main.temp_max - 273.15} C
          </ListGroup.Item>
          <ListGroup.Item>
            Low: {weather.main.temp_min - 273.15} C
          </ListGroup.Item>
          <ListGroup.Item>Pressure: {weather.main.pressure} </ListGroup.Item>
          <ListGroup.Item>Humidity: {weather.main.humidity}</ListGroup.Item>
        </ListGroup>
      ) : null}
    </div>
  );
}
export default observer(CurrentWeather);

We get the search keyword from the keywordStore which we will build and search for the current weather when keywordStore.keyword is updated by passing it in the array of the second argument of the React.useEffect function.

Once the data is retrieved, we set the data with the setWeather function and display the data in a ListGroup at the bottom of the page.

We wrap observer around our component in the last line so that we get the latest keyword value from keywordStore .

Next, we add a component for display the forecast, add Forecast.js in the src folder and add:

import React from "react";
import { observer } from "mobx-react";
import { searchForecast } from "./request";
import ListGroup from "react-bootstrap/ListGroup";
import Card from "react-bootstrap/Card";

function Forecast({ keywordStore }) {
  const [forecast, setForecast] = React.useState({});

  const getWeatherForecast = async keyword => {
    const response = await searchForecast(keyword);
    setForecast(response.data);
  };

  React.useEffect(() => {
    keywordStore.keyword && getWeatherForecast(keywordStore.keyword);
  }, [keywordStore.keyword]);

  return (
    <div>
      {Array.isArray(forecast.list) ? (
        <div>
          {forecast.list.map(l => {
            return (
              <Card body>
                <ListGroup>
                  <ListGroup.Item>Date: {l.dt_txt}</ListGroup.Item>
                  <ListGroup.Item>
                    Temperature: {l.main.temp - 273.15} C
                  </ListGroup.Item>
                  <ListGroup.Item>
                    High: {l.main.temp_max - 273.15} C
                  </ListGroup.Item>
                  <ListGroup.Item>
                    Low: {l.main.temp_min - 273.15} C
                  </ListGroup.Item>
                  <ListGroup.Item>Pressure: {l.main.pressure} C</ListGroup.Item>
                </ListGroup>
              </Card>
            );
          })}
        </div>
      ) : null}
    </div>
  );
}
export default observer(Forecast);

It’s very similar to CurrentWeather.js except that the data is in a list. So we map forecast.list into an array of Card s instead of just rendering it.

Next, we create a HomePage.js file in the src folder and add:

import React from "react";
import { Formik } from "formik";
import Form from "react-bootstrap/Form";
import Col from "react-bootstrap/Col";
import Button from "react-bootstrap/Button";
import { observer } from "mobx-react";
import * as yup from "yup";
import Tabs from "react-bootstrap/Tabs";
import Tab from "react-bootstrap/Tab";
import CurrentWeather from "./CurrentWeather";
import Forecast from "./Forecast";
import { geolocated } from "react-geolocated";
import { getLocationByLatLng } from "./request";

const schema = yup.object({
  keyword: yup.string().required("Keyword is required")
});

const buttonStyle = { marginRight: "10px" };

function HomePage({ keywordStore, coords }) {
  const [initialized, setInitialized] = React.useState(false);

  const handleSubmit = async evt => {
    const isValid = await schema.validate(evt);
    if (!isValid) {
      return;
    }
    localStorage.setItem("keyword", evt.keyword);
    keywordStore.setKeyword(evt.keyword);
  };

  const getWeatherCurrentLocation = async () => {
    const { latitude, longitude } = coords;
    const { data } = await getLocationByLatLng(latitude, longitude);
    const keyword = (
      data.results[0].address_components.find(c =>
        c.types.includes("locality")
      ) || {}
    ).long_name;
    localStorage.setItem("keyword", keyword);
    keywordStore.setKeyword(keyword);
  };

  const clear = () => {
    localStorage.clear();
    keywordStore.setKeyword("");
  };

  React.useEffect(() => {
    if (!initialized) {
      keywordStore.setKeyword(localStorage.getItem("keyword") || "");
      setInitialized(true);
    }
  }, [keywordStore.keyword]);

  return (
    <div className="page">
      <h1>Weather</h1>
      <Formik
        validationSchema={schema}
        onSubmit={handleSubmit}
        initialValues={{ keyword: localStorage.getItem("keyword") || "" }}
        enableReinitialize={true}
      >
        {({
          handleSubmit,
          handleChange,
          handleBlur,
          values,
          touched,
          isInvalid,
          errors
        }) => (
          <Form noValidate onSubmit={handleSubmit}>
            <Form.Row>
              <Form.Group as={Col} md="12" controlId="keyword">
                <Form.Label>City</Form.Label>
                <Form.Control
                  type="text"
                  name="keyword"
                  placeholder="City"
                  value={values.keyword || ""}
                  onChange={handleChange}
                  isInvalid={touched.keyword && errors.keyword}
                />
                <Form.Control.Feedback type="invalid">
                  {errors.keyword}
                </Form.Control.Feedback>
              </Form.Group>
            </Form.Row>
            <Button type="submit" style={buttonStyle}>
              Search
            </Button>
            <Button
              type="button"
              onClick={getWeatherCurrentLocation}
              style={buttonStyle}
              disabled={!coords}
            >
              Get Weather of Current Location
            </Button>
            <Button type="button" onClick={clear}>
              Clear
            </Button>
          </Form>
        )}
      </Formik>
      <br />
      <Tabs defaultActiveKey="weather">
        <Tab eventKey="weather" title="Current Weather">
          <CurrentWeather keywordStore={keywordStore} />
        </Tab>
        <Tab eventKey="forecast" title="Forecast">
          <Forecast keywordStore={keywordStore} />
        </Tab>
      </Tabs>
    </div>
  );
}

HomePage = observer(HomePage);

export default geolocated({
  positionOptions: {
    enableHighAccuracy: false
  },
  userDecisionTimeout: 5000
})(HomePage);

We use the React-Geolocated package here to add the geolocation functionality to our app. The package provides us with the geolocated higher-order component to let users get their current location if they allow our app to do so. We set enableHighAccuracy to false so that our app will respond faster by using a less accurate location, which is already accurate in most cases. userDecisionTimeout is set so that after 5 seconds, geolocation will be assumed to be disabled by the user.

Since we wrapped our HomePage component with the geolocated component, we will get the coords prop so that we get the location when geolocation is enabled. We add check for the coords prop in our ‘Get Weather of Current Location‘ button so that users can only use the geolocation functionality when they have geolocation enabled.

In the callback for the useEffect hook, we set the array in the second argument to watch for keywordStore.keyword changes so that it will re-render when that changes. We need this so that we get the latest keyword into our form via the intialValues prop of our Formik component. When we click the ‘Get Weather of Current Location‘ button, we get set the keyword in both local storage and the MobX store, so we can just watch the store to make it render the local storage’s keyword value.

In the getWeatherCurrentLocation function, we get the location with the Google Maps API, then get the city name by finding the component with locality in the types property, and get the long_name from that, then set the keyword in our MobX store for use by the previous components we created and also in local storage so that we see it in our form and keep the same value set after refreshing the value.

Next in index.js replace the existing code with:

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { KeywordStore } from "./store";
const keywordStore = new KeywordStore();

ReactDOM.render(
  <App keywordStore={keywordStore} />,
  document.getElementById("root")
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: [https://bit.ly/CRA-PWA](https://bit.ly/CRA-PWA)
serviceWorker.unregister();

to pass or MobX store into our app.

Then in we create requests.js in the src folder, and add the following:

const APIURL = "http://api.openweathermap.org";
const GOOGLE_MAP_API_URL = "https://maps.googleapis.com/maps/api/geocode/json";
const axios = require("axios");

export const searchWeather = loc =>
  axios.get(
    `${APIURL}/data/2.5/weather?q=${loc}&appid=${process.env.REACT_APP_APIKEY}`
  );

export const searchForecast = loc =>
  axios.get(
    `${APIURL}/data/2.5/forecast?q=${loc}&appid=${process.env.REACT_APP_APIKEY}`
  );

export const getLocationByLatLng = (lat, lng) =>
  axios.get(
    `${GOOGLE_MAP_API_URL}?latlng=${lat},${lng}&key=${process.env.REACT_APP_GOOGLE_APIKEY}`
  );

We have functions to make HTTP requests to get the weather and location with the Google API. To get the properties in process.env , put them in your .env file in your project’s root folder with those field properties as keys and the corresponding API keys as the values.

After that, create store.js in the src folder and add:

import { observable, action, decorate } from "mobx";

class KeywordStore {
  keyword = "";

  setKeyword(keyword) {
    this.keyword = keyword;
  }
}

KeywordStore = decorate(KeywordStore, {
  keyword: observable,
  setKeyword: action
});

export { KeywordStore };

to create our MobX Store. We have the keyword field that is watched by other components by wrappingobserver function outside our component when we export them. Also, we have the setKeyword function to set the latest values of the keyword field.

Finally, in index.js , replace the existing code with:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="logo192.png" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>Geolocation Weather App</title>
    <link
      rel="stylesheet"
     href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
      integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
      crossorigin="anonymous"
    />
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>

to change the title and add Bootstrap CSS.

After all the hard work, we run npm start to start our app. Then we get:

Categories
React

Creating Dropdown Menus with React Select

React Select is a dropdown menu library for React apps.

It supports many things that aren’t supported by regular dropdowns.

In this article, we’ll look at how to add menus with React Select.

Getting Started

We can install the package by running:

yarn add react-select

or:

npm i react-select

Then we can use it by writing:

import React from "react";
import Select from "react-select";

const options = [
  { value: "apple", label: "Apple" },
  { value: "orange", label: "Orange" },
  { value: "grape", label: "Grape" }
];

export default function App() {
  return (
    <>
      <Select options={options} />
    </>
  );
}

We add an options array with each entry having the value and label properties.

Then we pass that into the Select component.

Now the labels are displayed in the dropdown.

Animation

We can add animation with the makeAnimated function.

For instance, we can write:

import React from "react";
import Select from "react-select";
import makeAnimated from "react-select/animated";
const animatedComponents = makeAnimated();

const options = [
  { value: "apple", label: "Apple" },
  { value: "orange", label: "Orange" },
  { value: "grape", label: "Grape" }
];

export default function App() {
  return (
    <>
      <Select options={options} components={animatedComponents} />
    </>
  );
}

We pass the animatedComponents as the value of the components prop and we’ll see some animation.

Custom Styles

We can add custom styles with an object.

For example, we can write:

import React from "react";
import Select from "react-select";

const dot = (color = "#ccc") => ({
  alignItems: "center",
  display: "flex",

":before": {
    backgroundColor: color,
    borderRadius: 10,
    content: '" "',
    display: "block",
    marginRight: 8,
    height: 10,
    width: 10
  }
});

const styles = {
  control: styles => ({ ...styles, backgroundColor: "white" }),
  option: (styles, { data, isDisabled, isFocused, isSelected }) => {
    return {
      ...styles,
      backgroundColor: "green",
      color: "#ccc",
      cursor: isDisabled ? "not-allowed" : "default",
      ":active": {
        ...styles[":active"],
        backgroundColor: "orange"
      }
    };
  },
  input: styles => ({ ...styles, ...dot() }),
  placeholder: styles => ({ ...styles, ...dot() }),
  singleValue: (styles, { data }) => ({ ...styles, ...dot(data.color) })
};

const options = [
  { value: "apple", label: "Apple" },
  { value: "orange", label: "Orange" },
  { value: "grape", label: "Grape" }
];

export default function App() {
  return (
    <>
      <Select options={options} styles={styles} />
    </>
  );
}

We create a dot function that returns an object with some styles for a dot.

The :before has everything that creates a dot to the left of the text.

styles has various properties for the controls, options, input, placeholder, and selected values.

We can style according to the isFocused property which indicates whether the choice is in focus.

isSelected indicates whether the choice is selected.

data is the data of the item.

We then pass all that into the styles prop.

Multiselect

We can enable multiple selection with the isMulti prop.

For instance, we can write:

import React from "react";
import Select from "react-select";

const options = [
  { value: "apple", label: "Apple" },
  { value: "orange", label: "Orange" },
  { value: "grape", label: "Grape" }
];

export default function App() {
  return (
    <>
      <Select options={options} isMulti />
    </>
  );
}

Now we can pick multiple choices from the list.

Loading Choices Asynchronously

We can load choices in an async manner.

To do that, we use the AsyncSelect component:

import React from "react";
import AsyncSelect from "react-select/async";

const options = [
  { value: "apple", label: "Apple" },
  { value: "orange", label: "Orange" },
  { value: "grape", label: "Grape" }
];

const filterOptions = inputValue => {
  return options.filter(i =>
    i.label.toLowerCase().includes(inputValue.toLowerCase())
  );
};

const loadOptions = (inputValue, callback) => {
  setTimeout(() => {
    callback(filterOptions(inputValue));
  }, 1000);
};

export default function App() {
  const [inputValue, setInputValue] = React.useState("");
  const handleInputChange = newValue => {
    const inputValue = newValue.replace(/W/g, "");
    setInputValue(inputValue);
    return inputValue;
  };

  return (
    <div>
      <pre>inputValue: "{inputValue}"</pre>
      <AsyncSelect
        cacheOptions
        loadOptions={loadOptions}
        defaultOptions
        onInputChange={handleInputChange}
      />
    </div>
  );
}

We gave the filterOptions method to filter by the value.

loadOptions lets us load the options asynchronously.

It has the inputValue parameter which will be passed in when we type in something.

In the App component, we set the inputValue with the input box.

Also, we have the cacheOptions to cache the options.

defaultOptions determines when the request for the options is fired.

Conclusion

We can create a dropdown easily with React Select.

We can create a dropdown that supports multiple selections, styling, and async loading of options.