Next.js has support for native .css files that can be applied globally or at the component level.
pages/_app.js is the default app component used by Next.js to initialize each page. It is the starting point of all page components and acts as a blueprint for your pages. Consequently, if you want to apply global styling to your Next.js application, the stylesheets (.css files) must be included here.
/* styles/globals.css */ body { font-family: 'Roboto', 'Helvetica Neue', 'Helvetica', sans-serif; padding: 20px 20px 60px; margin: 0 auto; background: white; }
// pages/_app.jsx import '../styles.css' export default function MyApp({ Component, pageProps }) { return <Component {...pageProps} />; }
To add component-level CSS, we must use CSS modules. These are files that use the [name].module.css naming convention, can be imported anywhere in the application and help you scope the stylesheet to a particular component.
/* components/DangerousButton.module.css */ .danger { color: white; background-color: red; } .danger:hover { color: red; background-color: white; }
For instance, we can use the CSS module to add custom styles to a Material UI button.
// components/DangerousButton.jsx import styles from './DangerousButton.module.css' import { Button } from "@mui/material"; export function DangerousButton(props) { return ( <Button type="button" className={styles.danger} {...props}> { props.children } </Button> ) }
In this case, props.children is a special property that allows us to further pass the content inside the DangerousButton tag into the inner Button tag.
As an alternative to the classic CSS styling, Next.js offers a variety of solutions to style your React components using JavaScript:
While the majority of these solutions are provided through 3rd party libraries, inline styles and styled jsx are available out of the box.
To apply inline styling, we must use the style property.
// components/DangerousTypography.jsx import { Typography } from "@mui/material"; export function DangerousTypography(props) { return ( <Typography {...props} style={{ color: 'red' }}> {props.children} </Typography> ); }
Styled Components is one of the most popular libraries for adding style to your React components. It can be used globally or at the component level. However, we will only discuss the component-level styling with this library.
Conveniently, Material UI natively supports styled components, so we can easily use this library to overwrite the default styling of the MUI components.
// components/CoolCheckbox.jsx import * as React from 'react'; import { styled } from '@mui/material/styles'; import Checkbox from "@mui/material/Checkbox"; const StyledCheckbox = styled(Checkbox)` color: green!important; `; export default function CoolCheckbox({ props }) { return <StyledCheckbox {...props}/>; }
A/B testing is an accurate method of testing a particular idea, strategy or feature by performing user bucketing and simultaneously delivering two or more sources of truth to these buckets. Such solutions are usually integrated with analytics tools, so you can later assess which source of truth renders the best results.
Let's take a concrete example. You want to display 2 different versions of a button to your users - a blue one and a green one. You want to distribute this experiment evenly among your users, so you would like half of your users to see the green button and the other half the blue one. Moreover, you want this behaviour to be sticky. That means that if a user sees a green button once, they must keep seeing it green until the experiment is over. In the end, we would like to calculate the conversion rate of that particular button, so we need to track each click and record the version of the button (green or blue) for such click. Then, we must wait a sufficient amount of time so we have statistically relevant data for our conversion rate comparison. The winning button will be kept and the other one discarded.
Fortunately, there are 3rd party solutions that allow us to perform such an experiment easily. One of them is Flagsmith and we chose it for this lab because it offers a free plan.
Flagsmith allows the creation of feature flags, which are the cornerstones of A/B testing. They act as toggle buttons that turn various features on and off.
To use feature flags for A/B testing, however, we need to make them multi-variate. That means that our feature flags support variations, and each variation has a particular value assigned to it. The control value is the default value that gets sent to your application if the user is not yet identified (more on that later). Finally, these values can be weighted so you can decide the percentage of users that should participate in the experiment.
Just like Segment, Flagsmith requires users to be identified. That means that an explicit id must be provided for the user that currently uses the application. If we don't explicitly inform Flagsmith about the user's identity, that user will be considered anonymous and a random id will be assigned internally. Even though feature flags continue to work just fine for both identified and unidentified users, A/B testing can only be performed on identified users. That means that if an unidentified user uses our application, they will always see the feature version corresponding to the control value. In addition to the control value, the identified users can also see the weighted variations.
Users can also have traits and they are useful when you want to perform user segmentation. For instance, you can choose to perform an A/B test only for males, who have premium subscriptions and are at least 21 years old.
If you click on a user from the Users table, you can inspect its traits, manually delete them or add new ones, toggle features for that particular user or even change the values of those features. That can be really useful if you have a development account that you use to test different functionalities.
Flagsmith has native support for Next.js, as it can run both on the client and server sides.
The initialization code must be written in the pages/_app.jsx component on both the client-side and server-side, so the flags can be then accessed by all pages.
// pages/_app.jsx import * as React from 'react'; import Head from 'next/head'; import Layout from '../layouts/layout'; import '../styles/globals.css'; import flagsmith from "flagsmith/isomorphic"; import { FlagsmithProvider } from "flagsmith/react"; const FLAGSMITH_OPTIONS = { environmentID: 'YOUR_ENVIRONMENT_ID_HERE', enableAnalytics: true, preventFetch: true } MyApp.getInitialProps = async () => { await flagsmith.init(FLAGSMITH_OPTIONS); return { flagsmithState: flagsmith.getState() } } export default function MyApp({ Component, pageProps, flagsmithState }) { return ( <FlagsmithProvider serverState={flagsmithState} flagsmith={flagsmith} options={FLAGSMITH_OPTIONS}> <Head> <title>Lab 7</title> <meta name="description" content="This is the styling and A/B testing lab."/> <meta name="viewport" content="initial-scale=1, width=device-width"/> </Head> <Layout> <Component {...pageProps} /> </Layout> </FlagsmithProvider> ); }
Feature flags can then be accessed right from the React components.
// pages/index.jsx import React from 'react'; import { Container, Typography } from "@mui/material"; import { useFlags } from "flagsmith/react"; export default function Home() { const flags = useFlags(['google_signup', 'payment_message']); const isGoogleSignupEnabled = flags['google_signup']?.enabled; const paymentMessageSize = flags['payment_message']?.value; // 'big', 'huge' or 'control' return ( <Container maxWidth="sm"> <Typography variant="p" component="p"> Is Google signup enabled: {isGoogleSignupEnabled} </Typography> <Typography variant="p" component="p"> Payment message size: {paymentMessageSize} </Typography> </Container> ); }
Because we don't have a login system integrated into our app, we will identify a fictional user right after the app initialization code.
// pages/_app.jsx MyApp.getInitialProps = async () => { await flagsmith.init(FLAGSMITH_OPTIONS); await flagsmith.identify( 'user_90619', { fullName: 'Marie Curie', email: 'marie.curie@gmail.com', age: 23, gender: 'female', isPremiumUser: false } ); return { flagsmithState: flagsmith.getState() } }
The app uses the Cat Facts API and the GET requests are performed using Axios.
The necessary dependencies for the Styled Components and Flagsmith are also included in the project.
components/styled/BlueButton.jsx
.components/vanilla/GreenButton.jsx
. Add the corresponding CSS module in the same directory.pages/index.jsx
page component bold using inline styling.styles/globals.css
file into the app and remove the horizontal and top padding.pages/_app.jsx
.pages/index.jsx
. If the button_color feature flag is disabled, it should be a DangerousButton
. Conversely, the button should be a GreenButton
or BlueButton
depending on the feature flag value.
Please take a minute to fill in the feedback form for this lab.