How to Add in-App Messaging to Your React App

Christian Nwamba - Mar 14 '23 - - Dev Community

AWS Amplify provides an easy way to engage your customers through in-app messaging. With in-app messaging you can segment your customers, engage with them both when they are online and offline, and record analytics based on their engagement and behavior.

With AWS Amplify, you can quickly integrate in-app messaging functionality into your React app without the need to build everything from scratch. In this guide, I'll walk you through the steps needed to set up and configure AWS Amplify for your React app, and show you how to add in-app messaging.

What to Expect

You will watch me take a small shopping app and extend it so customers can get notified of a discount when they add a particular item to cart. Here’s a video that shows what you should be able to do by the end of this article:

Set up the Starter

Run the following command to setup the starter React project if you would like to follow along:

npx degit christiannwamba/inapp-react#starter
Enter fullscreen mode Exit fullscreen mode

cd into the cloned starter and run

npm install
Enter fullscreen mode Exit fullscreen mode

Once the dependencies have been installed, run the following to start the app:

npm run dev
Enter fullscreen mode Exit fullscreen mode

Starter Overview

The app we have just set up is a demo that generates positive word cards and allows you to shop for them. You can add the words to cart. You can increase the cart items. You can also decrease them.

There is also a cart page where you can either review your cart or update the items in the cart.

You can click on the select box to update the quantity of a specific item in the cart. You can also remove items from cart here. If you find what you like in the related products section, you can also add them to cart.

The cart has a summary section where you can review the total of items in your cart after taking tax and shipping cost into consideration.

The project is set up with Tailwind so all the styles are inline and you can hover on them if you have the VS Code extension to show the styles used.

The data is generated by randomizing a list of positive words and turning them into an array of objects. Each object has an ID, the positive word, a randomly generated color for the background of the card, and the price.

//lib/data.js
const positiveAdjectives = [
  "Cute",
  "Calm",
  //...
];
export function createData() {
  return Array.from(Array(36).keys())
    .sort(() => Math.random() - 0.5)
    .map((n) => {
      n = n * 10 === 0 ? 1 : n * 10;
      return {
        id: n,
        positiveAdjective: positiveAdjectives.pop(),
        hue: n,
        saturation: 49,
        light: 44,
        price: "$" + Math.floor(Math.random() * (400 - 100) + 100),
      };
    });
}
Enter fullscreen mode Exit fullscreen mode

I am caching the generated data in the localstorage using a custom hook so you don’t have to always generate the data every time the component that uses it renders.

//lib/data.js
import React from "react";
//...
export function useProducts() {
  const [products, setProducts] = React.useState([]);
  React.useEffect(() => {
    if (localStorage.getItem("products")) {
      setProducts(JSON.parse(localStorage.getItem("products")));
    } else {
      const productList = createData();
      localStorage.setItem("products", JSON.stringify(productList));
      setProducts(productList);
    }
  }, []);
  return products;
}
Enter fullscreen mode Exit fullscreen mode

The home page uses a list of cards from the data to render a Products component.

// pages/index.js
import React from "react";
import Layout from "@/components/Layout";
import Products from "@/components/Products";

import { useProducts } from "@/lib/data";

export default function Home() {

  const products = useProducts();

  return (
    <Layout>
      <div className="border-b border-gray-200 pt-16 pb-10">
        <h1 className="text-4xl font-bold tracking-tight text-gray-900">
          New Arrivals
        </h1>
        <p className="mt-4 text-base text-gray-500 max-w-2xl">
          Checkout out the latest release of inspirational cards, new and
          improved with more colors and punchy positive words!
        </p>
      </div>

      <Products products={products} />

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

The cart page fetches a list of cart from the local storage and renders them.

// pages/cart.js
import Layout from '@/components/Layout';
import { useCart } from 'react-use-cart';
import React from 'react';
import CartItems from '@/components/CartItems';
import OrderSummary from '@/components/OrderSummary';
import RelatedProducts from '@/components/RelatedProducts';
export default function Cart() {
  const [products, setProducts] = React.useState([]);
  const { items, updateItemQuantity } = useCart();
  React.useEffect(() => {
    setProducts(items);
  }, [items]);
  return (
    <Layout>
      <div className="bg-white">
        <main className="px-4 pt-16 pb-24 sm:px-6 lg:px-8">
          <h1 className="text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl">
            Shopping Cart
          </h1>
          <form className="mt-12 lg:grid lg:grid-cols-12 lg:items-start lg:gap-x-12 xl:gap-x-16">
            <CartItems
              products={products}
              updateItemQuantity={updateItemQuantity}
            />
            <OrderSummary products={products} />
          </form>
          <RelatedProducts />
        </main>
      </div>
    </Layout>
  );
}
Enter fullscreen mode Exit fullscreen mode

All the components in this project are for presentation purpose which means they mostly take props and render data based on the props. The only exception is the product component which adds and removes items from the cart.

The cart is managed by react use cart which allows you to add items to cart, remove items from cart, get items that are the cart and so on. The library knows how to keep track of our cart across different components because I wrapped the entire tree with a Cart provider in the pages/_app.js file.

// pages/_app.js
import { CartProvider } from "react-use-cart";

function App({ Component, pageProps }) {
  return (
    <CartProvider>
      <Component {...pageProps} />
    </CartProvider>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Now that we have an idea of what our starter app is doing, let’s add in-app notification to it.

Setup Amplify

Start by initializing an Amplify project using the following.

amplify init
Enter fullscreen mode Exit fullscreen mode

If you don’t have the Amplify CLI, follow this tutorial to set it up. It only takes 5 minutes.

Give the project a name and accept the default configuration. Choose your CLI’s AWS profile which you created when you set up the CLI.

This will also create a config file in src folder. The config file is ignored from git because your credentials live here.

To add support for in-app messaging to your app, run the following command.

amplify add notifications
Enter fullscreen mode Exit fullscreen mode

Choose In-App Messaging as the channel to enable and choose yes to allow unauthenticated users to send events since this is a demo.

You need to push the notification to AWS so the services that supports messaging can be provisioned for you:

amplify push
Enter fullscreen mode Exit fullscreen mode

Confirm that you want to push and wait for the push process to complete.

When you open you aws-exports.js config file, you should see it has been updated with the service access credentials.

Create a Campaign

When you pushed your Amplify app, a Pinpoint project was created for you. Pinpoint is the AWS service that Amplify uses to segment your users, and determine which type of message to show when they perform a specific action in your app. Before you can create a segment you need to setup a campaign for the segment.

Open the AWS Console and search for “Pinpoint”.

Open Pinpoint and choose the project we created from the list of projects.

Navigate to Campaigns and click Create Campaign.

A campaign is a set of rules you give to Pinpoint and it follows those rules to assist you in messaging a given audience.

The first set of rules are about the campaign type and the channel you want to reach the audience. Give the campaign a name, leave campaign type as Standard and then choose In-app messaging as the channel and leave the prioritization to default:

Click Next to define the next set of rules.

The second set of rules allows you to define a segment of audience you want to reach. With segmentation, you can reach a segment of your audience or customers that fit into a given profile. For example in the sample app I have built, you want to show an In-app message when a user adds to the cart a specific item.

Choose Create a segment, give the segment a name and click Next.

Confirm when Pinpoint asks you if you understand that a segment might include multiple channels.

The next set of rules is specify the actual message you want to send to the audience. You can create both the content and how you want it to look in Poinpoint. This is very handy if you are not a developer but need to send out messages to your audience.

Click Create a new In-app message and give the message a header.

Write a more detailed message in the Message box:

You can see that as I enter the header and message values, the preview updates to show you how it will look like when you trigger the event.

You can customize this look to your taste. For example, change the layout so the message appears at the bottom of the screen.

Click Next to move to the last set of rules. This is how you tell the campaign when to launch and when it should send a message.

To send a message you only need to give it an event name. When it receives this event, it will trigger the event.

You can also use attributes to tell the Campaign to fire only when an event’s attribute contains a set of information.

Since I have a shop where you buy positive words, I want to only trigger the event if the item your bought is either the Happy adjective or the Lucky adjective.

Add the Happy Adjective:

Click Add new attribute to add the Luck adjective:

You need to tell the Campaign when it should start. Pinpoint allows you to start the campaign as near as 15 minutes from your current time:

Click Next to review your campaign and click Launch campaign to launch it.

You should see the campaign in your list of campaigns:

Configure In-app Messaging

We have a campaign and we have the credentials in our React project. Let’s tie both together so our app is set up for sending in-app messages.

Install Amplify JavaScript library and the Amplify UI Library:

npm install @aws-amplify/ui-react aws-amplify
Enter fullscreen mode Exit fullscreen mode

Import the Amplify library and your Amplify config file in the _app.js file:

// pages/_app.js
import { Amplify } from "aws-amplify";
import awsconfig from "../src/aws-exports";
Enter fullscreen mode Exit fullscreen mode

Configure the SDK with the config object:

// pages/_app.js
Amplify.configure(awsconfig);
Enter fullscreen mode Exit fullscreen mode

We need to wrap the app with the in app messaging higher order component so the app has access to our notification set up and can send notification from any where in the app tree. Import withInAppMessaging:

// pages/_app.js
import { withInAppMessaging } from "@aws-amplify/ui-react";
Enter fullscreen mode Exit fullscreen mode

Wrap App with it:

// pages/_app.js
export default withInAppMessaging(App);
Enter fullscreen mode Exit fullscreen mode

To use custom component for the notification, import the Notification component which came with the starter project:

// pages/_app.js
import Notification from "@/components/Notification";
Enter fullscreen mode Exit fullscreen mode

Pass the component to the components object which you can set as the second argument for withInAppMessaging:

// pages/_app.js
export default withInAppMessaging(App, {components: {BannerMessage: Notification}});
Enter fullscreen mode Exit fullscreen mode

If you want to use the default UI component provided by Amplify, you can omit the second argument.

Sync Messages

You need to sync the messages in the user’s client whenever they are online. This does not trigger the message but makes it available offline so it can be triggered whenever a user interacts with your app and performs a specific action.

You can sync the messages anywhere in your app tree but since we intend to trigger the message events from the Product component, we can sync the messages there.

Open the Product component and import Notifications from the Amplify library.

// components/Product.js
import { Notifications } from "aws-amplify";
Enter fullscreen mode Exit fullscreen mode

Grab the InAppMessaging object from it:

// components/Product.js
const { InAppMessaging } = Notifications;
Enter fullscreen mode Exit fullscreen mode

Call syncMessages inside the Product component’s useEffect.

// components/Product.js
React.useEffect(() => {
  InAppMessaging.syncMessages();
}, []);
Enter fullscreen mode Exit fullscreen mode

The messages will be downloaded to your localStorage and can be triggered when online or offline.

Trigger Messages

We want the following conditions to be met to trigger the message:

  1. The first condition is that the item is not already in the cart
  2. (we can check this inCart from react-use-cart) and
  3. The adjectives match the adjectives we set in our attributes when we set up the campaign

Add the following to the addToCart function:

const addToCart = (product) => {
  if (!inCart(product.id)) { // Only if we have not added to cart
    InAppMessaging.dispatchEvent({
      name: "cart_15",
      attributes: { adj: product.positiveAdjective }, // Only allowed adjectives
    });
  }
  //...
};
Enter fullscreen mode Exit fullscreen mode

Try adding either Be Lucky or Be Happy card to the cart and you should get see a notification:

Clean Up

To ensure that you don’t have any unused resources in you AWS account, run the following command to delete all the resources that were created in this project if you don’t intend to keep them.

amplify delete
Enter fullscreen mode Exit fullscreen mode

Conclusion

As you’ve seen, by following the steps outlined in this guide, you can quickly set up and configure AWS Amplify to add in-app messaging capabilities to your React app. New to AWS
Amplify? You can try it on the AWS Free Tier. Once implemented, you can start experimenting with engaging with your users. Spend sometime creating campaigns and segmenting your customers so you can take full advantage of what Amplify and Pinpoint can offer.

You can visit Amplify's documentation to learn more about In-App messaging here

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .