Blogs/Technology

How to Connect AWS IoT Core to React Native Using MQTT

Written byMurtuza Kutub
Aug 10, 2026
10 Min Read
How to Connect AWS IoT Core to React Native Using MQTT Hero
Too Long? Read This First

- React Native connects to AWS IoT Core through MQTT over Secure WebSockets, avoiding the need to embed device certificates in the application.
- Amazon Cognito provides temporary AWS credentials, while IAM and AWS IoT policies determine which topics each identity can access.
- iot:Subscribe uses a topicfilter ARN, whereas iot:Publish and iot:Receive use topic ARNs. Using the wrong resource type causes authorization failures.
- MQTT messages are not permanently stored by default. Use retained messages, persistent sessions, Device Shadows, or another datastore when recovery is required.
- Mobile MQTT connections can be interrupted when the application enters the background or changes networks. Reconnection and missed-message recovery must be handled deliberately.
- A shared PubSub instance and proper subscription cleanup prevent duplicate connections, observers, and message processing.
- Production policies should restrict users to specific topics instead of using "Resource": "*".

Building real-time communication into a React Native application is rarely difficult because of MQTT itself. The challenge lies in correctly combining AWS IoT Core, Cognito credentials, topic permissions, WebSocket authentication, and the mobile application lifecycle.

We encountered this while integrating MQTT into a React Native application. Several general-purpose MQTT libraries worked in browsers or Node.js but became unreliable when combined with AWS authentication and React Native. The integration became more predictable once authentication, authorization, and MQTT transport were treated separately.

This guide explains that architecture and demonstrates how to connect React Native to AWS IoT Core using Cognito and Amplify PubSub.

What Is MQTT?

MQTT, or Message Queuing Telemetry Transport, is a lightweight publish-and-subscribe protocol commonly used for device telemetry, live status updates, remote commands, and sensor data.

An MQTT system has three primary participants:

  • A publisher sends messages to a topic.
  • A subscriber listens for messages sent to a topic.
  • A broker receives messages and routes them to matching subscribers.

AWS IoT Core acts as the broker.

For example, a temperature sensor could publish to:

devices/sensor-42/temperature

A React Native application subscribed to the same topic would receive new readings as they are published.

Topics are hierarchical strings separated by forward slashes. A clear topic structure makes authorization and message routing easier to manage:

devices/{deviceId}/telemetry
devices/{deviceId}/commands
users/{userId}/notifications

MQTT vs Firebase Cloud Messaging

MQTT and Firebase Cloud Messaging can both deliver messages, but they solve different problems.

MQTTFirebase Cloud Messaging
Maintains a broker connectionUses platform push-notification services
Supports two-way publish-and-subscribe messagingPrimarily sends notifications or data messages to applications
Suitable for live telemetry and commandsSuitable for alerts and background notifications
Uses hierarchical topicsUses registration tokens and notification topics
Connections may pause when the app is backgroundedDesigned to reach backgrounded applications
Maintains a broker connection
Firebase Cloud Messaging
Uses platform push-notification services
1 of 5

For continuous foreground communication, MQTT is generally the better fit. For notifying users while the application is suspended, push notifications are more reliable.

Many production applications use both: MQTT while the application is active and push notifications for important background events.

How React Native Connects to AWS IoT Core

AWS IoT Core supports MQTT over TLS and MQTT over Secure WebSockets. A React Native application can connect through Secure WebSockets on port 443 using AWS Signature Version 4 authentication.

The connection works as follows:

  1. The user authenticates through Amazon Cognito.
  2. A Cognito Identity Pool provides temporary AWS credentials.
  3. Amplify uses those credentials to sign the WebSocket request.
  4. AWS IoT Core authenticates the connection.
  5. IAM and AWS IoT policies authorize the client and requested topics.
  6. The application publishes or receives MQTT messages.

Cognito credentials are temporary, so the application does not require permanent AWS access keys or private device certificates in its bundle.

The AWS IoT authorization documentation confirms that MQTT over WebSockets supports Signature Version 4 authentication with Amazon Cognito identities.

Prerequisites

Before implementing the connection, you need:

  • A React Native project
  • An AWS account
  • An AWS IoT Core endpoint
  • An Amazon Cognito Identity Pool
  • Cognito authentication configured in the application
  • An IAM policy for the Cognito role
  • An AWS IoT policy for authenticated identities
  • Node.js and npm installed locally

A Cognito User Pool and an Identity Pool are not interchangeable. A User Pool authenticates users, while an Identity Pool provides temporary AWS credentials for accessing services such as AWS IoT Core.

Step 1: Find Your AWS IoT Core Endpoint

Open the AWS IoT Core console and navigate to Settings. Copy the device data endpoint for the Region used by your application.

A current ATS endpoint generally looks like this:

a1b2c3d4e5f6g7-ats.iot.ap-south-1.amazonaws.com

Amplify PubSub expects the Secure WebSocket version:

wss://a1b2c3d4e5f6g7-ats.iot.ap-south-1.amazonaws.com/mqtt

The Cognito Identity Pool, IoT endpoint, and policies must use the intended AWS Regions. A Region mismatch is a common cause of authentication failures and disrupted connections.

Step 2: Design the MQTT Topics

Define the topic structure before creating the policy. This example uses:

devices/demo/status
devices/demo/commands

The React Native application receives status messages and publishes commands.

In a production application, replace demo with an authorized device identifier. Your backend should verify that the signed-in user can access that device before granting the corresponding AWS IoT permissions.

Avoid placing confidential information in topic names because topics can appear in policies, logs, metrics, and operational tools.

Step 3: Create a Scoped AWS IoT Policy

Avoid giving every identity access to every MQTT action and topic through "Resource": "*". AWS recommends following the principle of least privilege and limiting each identity to known clients and topic sets.

Let’s Build Your React Native App Together!

We build powerful React Native apps that run smoothly on iOS and Android — fast, reliable, and ready to scale.

Create a policy similar to this:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": "arn:aws:iot:ap-south-1:123456789012:client/*"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Subscribe",
      "Resource": [
        "arn:aws:iot:ap-south-1:123456789012:topicfilter/devices/demo/status"
      ]
    },
    {
      "Effect": "Allow",
      "Action": "iot:Receive",
      "Resource": [
        "arn:aws:iot:ap-south-1:123456789012:topic/devices/demo/status"
      ]
    },
    {
      "Effect": "Allow",
      "Action": "iot:Publish",
      "Resource": [
        "arn:aws:iot:ap-south-1:123456789012:topic/devices/demo/commands"
      ]
    }
  ]
}

Replace:

  • ap-south-1 with your AWS IoT Region.
  • 123456789012 with your AWS account ID.
  • demo with the device identifier used by your application.

Notice that iot:Subscribe uses topicfilter, while iot:Publish and iot:Receive use topic. Confusing these resource types causes authorization failures even when the topic string appears correct.

The wildcard under client/* accommodates libraries that generate MQTT client IDs dynamically. If your client supports predictable IDs, restrict this ARN further in production.

AWS provides more examples in its publish and subscribe policy documentation.

Step 4: Configure Cognito Permissions

A Cognito Identity Pool is associated with authenticated and, optionally, unauthenticated IAM roles. The authenticated role must permit the required AWS IoT data-plane actions.

For authenticated Cognito identities, authorization may involve both:

  • An IAM policy attached to the Identity Pool’s authenticated role
  • An AWS IoT policy attached to the individual Cognito identity

AWS IoT evaluates both layers and grants only the permissions allowed by both. If either policy omits an operation, the connection or message request may fail.

After authentication, retrieve the Cognito identity ID:

import {fetchAuthSession} from 'aws-amplify/auth';

export async function getCognitoIdentityId() {
  const session = await fetchAuthSession();

  if (!session.identityId) {
    throw new Error('No Cognito identity ID is available');
  }

  return session.identityId;
}

Attach the IoT policy from trusted backend infrastructure. For development, the equivalent AWS CLI command is:

aws iot attach-policy \
  --policy-name ReactNativeDevicePolicy \
  --target "COGNITO_IDENTITY_ID"

Do not embed administrator credentials in React Native to call AttachPolicy. A trusted backend or provisioning process must determine which users can access which devices.

AWS explains this requirement in its guide to Amazon Cognito identities with AWS IoT Core.

Step 5: Install Amplify PubSub

Install Amplify, PubSub, and the required React Native dependencies:

npm install \
  aws-amplify \
  @aws-amplify/pubsub \
  @aws-amplify/react-native \
  @react-native-community/netinfo \
  @react-native-async-storage/async-storage \
  react-native-get-random-values \
  react-native-url-polyfill

For iOS, install the native pods:

npx pod-install

Keep aws-amplify and @aws-amplify/pubsub on compatible versions. Amplify JavaScript v6 distributes PubSub as a separate package rather than including it in the main package.

Compatibility note: The current PubSub package remains available, but its detailed AWS IoT setup documentation is under Amplify Gen 1, which is in maintenance mode. Verify the package’s current support status when adopting it for a new long-term project.

Step 6: Configure Amplify

Configure Amplify once near the application entry point.

New Amplify Gen 2 projects generate amplify_outputs.json:

import 'react-native-get-random-values';
import 'react-native-url-polyfill/auto';

import {Amplify} from 'aws-amplify';
import outputs from './amplify_outputs.json';

Amplify.configure(outputs);

If you are maintaining an Amplify Gen 1 project, import its generated configuration instead:

import 'react-native-get-random-values';
import 'react-native-url-polyfill/auto';

import {Amplify} from 'aws-amplify';
import amplifyconfig from './amplifyconfiguration.json';

Amplify.configure(amplifyconfig);

Use only the configuration file generated by your project. Do not include both examples in the same application.

The selected configuration must contain the Cognito resources required to obtain an identity and temporary AWS credentials.

Step 7: Create a Shared PubSub Client

Create src/services/pubsub.ts:

import {PubSub} from '@aws-amplify/pubsub';

const AWS_REGION = 'ap-south-1';

const AWS_IOT_ENDPOINT =
  'wss://a1b2c3d4e5f6g7-ats.iot.ap-south-1.amazonaws.com/mqtt';

export const pubsub = new PubSub({
  region: AWS_REGION,
  endpoint: AWS_IOT_ENDPOINT,
});

Replace the Region and endpoint with your AWS values.

Keeping one exported PubSub instance prevents different screens from opening independent connections to the same broker. This is particularly important when navigation repeatedly mounts and unmounts components.

The endpoint and Region are configuration values, not credentials. Private keys, AWS secret access keys, and administrator credentials must never be stored in a React Native bundle.

Step 8: Subscribe to an MQTT Topic

Create src/components/DeviceStatus.tsx:

import {useEffect, useState} from 'react';
import {StyleSheet, Text, View} from 'react-native';

import {pubsub} from '../services/pubsub';

type DeviceStatusMessage = {
  online?: boolean;
  temperature?: number;
};

export default function DeviceStatus() {
  const [message, setMessage] =
    useState<DeviceStatusMessage | null>(null);

  useEffect(() => {
    const subscription = pubsub
      .subscribe({topics: 'devices/demo/status'})
      .subscribe({
        next: data => {
          setMessage(data as DeviceStatusMessage);
        },
        error: error => {
          console.error('MQTT subscription failed:', error);
        },
      });

    return () => {
      subscription.unsubscribe();
    };
  }, []);

  return (
    <View style={styles.container}>
      <Text style={styles.heading}>Device status</Text>

      <Text>
        {message
          ? JSON.stringify(message)
          : 'Waiting for an MQTT message…'}
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    padding: 16,
  },
  heading: {
    fontSize: 20,
    fontWeight: '600',
    marginBottom: 8,
  },
});

The cleanup function is important. Without it, repeatedly opening the screen can create duplicate observers and cause a single MQTT message to be processed multiple times.

Amplify PubSub supports JSON-serializable message payloads. If your broker publishes binary payloads, you will need a different client or an explicit serialization strategy.

Step 9: Publish an MQTT Message

Create src/services/deviceCommands.ts:

import {pubsub} from './pubsub';

export async function turnDeviceOn() {
  await pubsub.publish({
    topics: 'devices/demo/commands',
    message: {
      command: 'TURN_ON',
      requestedAt: new Date().toISOString(),
    },
  });
}

Call the function from a component:

async function handleTurnOn() {
  try {
    await turnDeviceOn();
    console.log('Command published');
  } catch (error) {
    console.error('Unable to publish command:', error);
  }
}

A successful publish() call means AWS IoT accepted the message. It does not prove that the physical device received, processed, or completed the command.

For operations requiring confirmation, create an acknowledgement topic:

devices/demo/commands/acknowledgements

Include a unique request ID in the command and acknowledgement so the application can match the response to the original action.

Step 10: Test the Connection

Before debugging React Native, verify the AWS configuration independently:

  1. Open AWS IoT Core.
  2. Navigate to the MQTT test client.
  3. Subscribe to devices/demo/commands.
  4. Run the React Native application.
  5. Publish a command from the application.
  6. Confirm that it appears in the MQTT test client.
  7. Publish JSON to devices/demo/status.
  8. Confirm that the application receives it.

Example status message:

{
  "online": true,
  "temperature": 24.6
}

If the AWS test client works but React Native does not, check the Cognito credentials, WebSocket endpoint, policy attachment, topic authorization, and Region configuration.

Let’s Build Your React Native App Together!

We build powerful React Native apps that run smoothly on iOS and Android — fast, reliable, and ready to scale.

Are MQTT Messages Stored?

MQTT messages should not be treated as permanent storage. A subscriber that is disconnected may miss messages published while it is offline.

AWS IoT Core provides several options for recovery:

  • Retained messages preserve the latest retained value for an exact topic.
  • Persistent sessions can preserve session state and eligible queued messages.
  • Device Shadows store a device’s desired and reported state.
  • IoT Rules can route messages to DynamoDB, S3, Lambda, Timestream, or other services.

Choose the mechanism based on the data. Losing an occasional live temperature update may be acceptable, while a critical command may require persistence, acknowledgement, idempotency, and retry handling.

Handling the React Native App Lifecycle

Mobile MQTT connections are not continuously reliable. iOS and Android can suspend background applications, networks can change, and temporary AWS credentials can expire.

Amplify PubSub can reconnect when connectivity returns, but messages published during disconnection are not automatically replayed. Applications that require continuity should retrieve the latest state after reconnecting.

In practice, we recommend:

  • Displaying the current connection state to the user
  • Unsubscribing when a component no longer needs a topic
  • Maintaining one PubSub instance per endpoint
  • Recovering authoritative state through an API or Device Shadow
  • Making commands idempotent so retries are safe
  • Testing foreground, background, offline, and network-switching behaviour
  • Avoiding uncontrolled manual reconnection loops

Repeated reconnection without backoff can drain the battery, generate noisy logs, and continuously repeat failed authentication requests.

Common AWS IoT and React Native Errors

The WebSocket closes immediately

This often indicates incorrect credentials, a missing IoT policy attachment, a Region mismatch, or denied iot:Connect permission. Verify the Cognito identity and inspect AWS IoT logs.

The application connects but cannot subscribe

Check that the policy grants iot:Subscribe on a topicfilter ARN. Using a topic ARN does not authorize an MQTT subscription request.

The application subscribes but receives nothing

Receiving messages requires iot:Receive on the matching topic ARN. Also confirm that the publisher uses the exact same case-sensitive topic and AWS Region.

Publishing returns an authorization error

Verify that iot:Publish covers the complete topic ARN. An additional, missing, or differently capitalized path segment represents a different AWS IoT resource.

Messages arrive more than once

The component may be creating subscriptions without disposing of them. Store the returned subscription and call unsubscribe() in the React useEffect cleanup function.

Messages are missing after the app returns

MQTT subscriptions may reconnect, but missed messages are not automatically recovered. Retrieve current state from an API, retained message, datastore, or AWS IoT Device Shadow.

Production Security Checklist

Before releasing the application:

  • Avoid "Resource": "*" for production topic access.
  • Never embed permanent AWS credentials or private certificates.
  • Authenticate users before granting access to private device topics.
  • Attach IoT policies through trusted backend infrastructure.
  • Verify that each user is authorized to access the requested device.
  • Separate command, telemetry, and status topics.
  • Monitor rejected connections and authorization failures.
  • Remove permissions when a user loses access to a device.
  • Limit message size, frequency, and accepted command types.
  • Avoid placing confidential information in topic names.

The policy should reflect the data each identity genuinely requires, not simply what makes the first connection easiest.

FAQ

Can React Native connect directly to AWS IoT Core?

Yes. React Native can connect through MQTT over Secure WebSockets with Signature Version 4 authentication. Cognito supplies temporary credentials without placing permanent AWS access keys inside the application.

Should a React Native app contain an AWS IoT certificate?

Generally, no. A shared certificate extracted from the application could compromise every installation using it. Cognito-issued temporary credentials are usually safer for user-facing mobile applications.

Why does an authenticated Cognito user receive an authorization error?

The identity may require permission from both the Identity Pool’s IAM role and an AWS IoT policy attached to that Cognito identity. Missing either layer can deny the operation.

Does AWS IoT Core store every MQTT message?

No. Use retained messages, persistent sessions, Device Shadows, IoT Rules, or another datastore when messages must remain available after a subscriber disconnects or reconnects.

Does MQTT continue working when a React Native app is backgrounded?

Not reliably. Mobile operating systems may suspend the application and interrupt its connection. Use push notifications for important background alerts and recover the current state after MQTT reconnects.

Conclusion

AWS IoT Core can provide effective real-time messaging for React Native, but establishing the MQTT connection is only one part of the implementation. Authentication, topic authorization, lifecycle handling, and state recovery determine whether the integration is secure and dependable.

The key improvements over older implementations are using temporary Cognito credentials, connecting over Secure WebSockets, restricting permissions to specific topics, maintaining a shared PubSub client, cleaning up subscriptions, and planning for messages missed while the application is offline.

Author-Murtuza Kutub
Murtuza Kutub
LinkedIn

A product development and growth expert, helping founders and startups build and grow their products at lightning speed with a track record of success. Apart from work, I love to Network & Travel.

Share this article

Phone

Next for you

8 Best GraphQL Libraries for Node.js in 2025 Cover

Technology

Aug 4, 202613 min read

8 Best GraphQL Libraries for Node.js in 2025

8 Best GraphQL Libraries for Node.js in 2026 Too Long? Read This First - Choose Apollo Server when you need a mature ecosystem, GraphOS integration, plugins, or Apollo Federation. - Choose GraphQL Yoga for a modern, portable server with Fetch API compatibility and built-in support for subscriptions over Server-Sent Events. - Choose Mercurius when your application already uses Fastify and runtime efficiency is a major priority. - Use GraphQL.js when you need the official JavaScript implementati

9 React Native Animation Libraries and Tools Compared Cover

Technology

Aug 4, 202615 min read

9 React Native Animation Libraries and Tools Compared

Too Long? Read This First - Use React Native Reanimated for gesture-driven, interruptible, and performance-sensitive interface animations. - Use the built-in Animated API for simple fades, transforms, and timed sequences without another dependency. - Pair React Native Gesture Handler with Reanimated for swipes, dragging, pinching, rotation, and other touch-driven experiences. - Use Lottie React Native for non-interactive motion graphics supplied by designers. - Choose React Native Skia for cust

9 Critical Practices for Secure Web Application Development Cover

Technology

Aug 4, 202616 min read

9 Critical Practices for Secure Web Application Development

Too Long? Read This First - Define security requirements and model threats before implementation begins. - Treat authentication, account recovery, and MFA as one complete identity system. - Apply server-side authorization to every protected action and object. - Prevent injection with parameterized APIs, structured validation, safe output handling, and restricted outbound requests. - Protect sessions and tokens throughout their complete lifecycle. - Minimise sensitive data and manage encryption