IoT Tutorials

MQTT Client Libraries: Choosing the Right One for Every Language

A practical comparison of maintained MQTT client libraries for Python, JavaScript, Go, Java, C#, Rust, C, embedded and mobile, with a minimal connect-and-publish snippet for each one, tested against broker.connectmq.com.

Code editor windows in several programming languages connected to one broker, illustrating MQTT client libraries
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

Choosing between MQTT client libraries is the first real engineering decision in any MQTT project, and the wrong pick shows up later as reconnect bugs, missing MQTT 5 features or a blocked event loop. This guide compares the maintained options for Python, JavaScript, Go, Java, C#, Rust, C, microcontrollers and mobile, and gives you a minimal snippet for each one that connects to the free broker broker.connectmq.com. By the end you will know which library fits your stack and what to check before you commit to it.

What an MQTT Client Library Actually Does

An MQTT client library hides the binary protocol behind a few calls: connect, subscribe, publish, disconnect. Under the hood it does much more, and these hidden jobs are where libraries differ:

  • Packet encoding and decoding for MQTT 3.1.1 and, in better libraries, MQTT 5.0 properties such as reason codes, message expiry and user properties.
  • Keep-alive pings so the broker knows the connection is healthy.
  • QoS state machines that store in-flight QoS 1 and QoS 2 messages and retry them after a reconnect.
  • Reconnect logic with backoff, plus resubscribing when the session was not persisted.
  • Transport handling for TCP, TLS and WebSocket.
  • Threading or async integration with your runtime: a background thread, an event loop, goroutines or callbacks.

A library that gets the protocol encoding right but handles reconnects poorly will pass every demo and fail in the field. Keep that in mind when you read the comparison below. If the broker side is still fuzzy, read what an MQTT broker does first; every library here is just a client of one.

How to Evaluate MQTT Client Libraries

Use this checklist before adopting any library, regardless of language:

  1. Maintenance. Look at recent releases and how issues are handled. MQTT itself is stable, but TLS stacks, runtimes and platforms move constantly.
  2. Protocol version. Do you need MQTT 5? Shared subscriptions, message expiry, request/response and detailed reason codes only exist there.
  3. Transports. Browsers need WebSocket; locked-down corporate networks often need WSS on 443-style ports; devices usually want TLS on 8883.
  4. Concurrency model. Callback-based, thread-based or async/await. Pick what matches the rest of your codebase, not what looks shortest in a README.
  5. Reconnect and offline buffering. Does it reconnect automatically? Does it queue publishes while offline, and is that queue bounded?
  6. Persistence of in-flight messages. Memory only, or file-backed? This matters for QoS 1 and 2 across process restarts.
  7. Footprint. On microcontrollers, RAM and flash decide everything; on servers, they rarely matter.
  8. License. Most popular libraries use EPL/EDL, MIT or Apache 2.0, but check before shipping a commercial product.

MQTT Client Libraries at a Glance

Language / platform Library MQTT 5 Model Good fit for
Python paho-mqtt 2.x Yes Background thread or manual loop Scripts, gateways, Raspberry Pi
Python aiomqtt Yes asyncio Async services, FastAPI backends
JavaScript / TypeScript MQTT.js 5.x Yes Event emitter, promises Node.js and browsers (WebSocket)
Go Eclipse Paho Go (paho.mqtt.golang) No (3.1.1) Tokens, goroutines Mature 3.1.1 services
Go Eclipse Paho paho.golang Yes Context-based New MQTT 5 services
Java / Kotlin Eclipse Paho Java (mqttv3, mqttv5) Yes (v5 package) Sync and async clients JVM backends, Android
Java / Kotlin HiveMQ MQTT Client Yes Blocking, async, reactive Reactive JVM stacks
C# / .NET MQTTnet Yes async/await ASP.NET Core, MAUI, Unity
Rust rumqttc Yes async (tokio) or sync Edge services, gateways
C Eclipse Paho C, libmosquitto Yes Sync and async Linux devices, native apps
Arduino / ESP32 PubSubClient, AsyncMqttClient No (3.1.1) Loop-based / async callbacks Microcontrollers
ESP-IDF esp-mqtt Yes Event handler Production ESP32 firmware
Swift CocoaMQTT Yes Delegate / closures iOS and macOS apps
Dart / Flutter mqtt_client No (3.1.1) Streams Cross-platform mobile

The table reflects the libraries' documented capabilities at the time of writing. Always confirm against the project's current release notes, because MQTT 5 support in particular has been arriving in stages. The Eclipse Paho project page at eclipse.dev/paho is a good starting point for the Paho family.

Python: paho-mqtt and aiomqtt

paho-mqtt is the default Python choice and works almost everywhere, from a Raspberry Pi to a container. Version 2.x introduced a breaking change: you must pass a callback API version, and the v2 callbacks receive reason codes and properties. Most "it does not work" reports come from copying 1.x examples into a 2.x install, which is covered in detail in the paho-mqtt 2.0 CallbackAPIVersion fix.

import uuid
import paho.mqtt.client as mqtt

TOPIC = "connectmq/demo/k3x8p1/libs/python"

def on_connect(client, userdata, flags, reason_code, properties):
    print("Connected:", reason_code)
    client.subscribe(TOPIC, qos=1)
    client.publish(TOPIC, "hello from paho-mqtt", qos=1)

def on_message(client, userdata, msg):
    print(msg.topic, msg.payload.decode())

client = mqtt.Client(
    mqtt.CallbackAPIVersion.VERSION2,
    client_id=f"libs-py-{uuid.uuid4().hex[:8]}",
    protocol=mqtt.MQTTv5,
)
client.on_connect = on_connect
client.on_message = on_message
client.tls_set()
client.connect("broker.connectmq.com", 8883, keepalive=30)
client.loop_forever()

aiomqtt wraps paho in an idiomatic asyncio API with async with and async for. If your service already runs an event loop, use it instead of mixing threads and coroutines. The full walkthroughs are in our paho-mqtt Python tutorial and the aiomqtt asyncio guide.

JavaScript and TypeScript: MQTT.js

MQTT.js is the de facto library for Node.js and the browser, ships TypeScript types and supports MQTT 5. In Node.js it connects over TCP or TLS; in the browser it uses WebSocket, so the URL scheme is the only thing that changes.

import mqtt from "mqtt";

const clientId = "libs-js-" + Math.random().toString(16).slice(2, 10);
const topic = "connectmq/demo/k3x8p1/libs/javascript";

// Node.js: mqtts://broker.connectmq.com:8883
// Browser: wss://broker.connectmq.com:8084/mqtt
const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
  clientId,
  protocolVersion: 5,
});

client.on("connect", async () => {
  await client.subscribeAsync(topic, { qos: 1 });
  await client.publishAsync(topic, "hello from MQTT.js", { qos: 1 });
});

client.on("message", (t, payload) => console.log(t, payload.toString()));
client.on("error", (err) => console.error(err.message));

MQTT.js reconnects automatically by default (reconnectPeriod controls the delay). In the browser, remember that an HTTPS page can only open wss:// connections. Framework-specific patterns live in MQTT.js with Node.js and MQTT in the browser with JavaScript.

Go: Eclipse Paho

Go has two Eclipse projects. paho.mqtt.golang is the long-established MQTT 3.1.1 client used in a great many production services. paho.golang is the newer MQTT 5 implementation with an autopaho package that manages reconnection for you. For a new MQTT 5 project, start with paho.golang; for broad 3.1.1 compatibility and the largest body of examples, the classic client is still a solid choice.

package main

import (
	"crypto/tls"
	"fmt"
	"math/rand"
	"time"

	mqtt "github.com/eclipse/paho.mqtt.golang"
)

func main() {
	topic := "connectmq/demo/k3x8p1/libs/go"
	opts := mqtt.NewClientOptions().
		AddBroker("tls://broker.connectmq.com:8883").
		SetClientID(fmt.Sprintf("libs-go-%06d", rand.Intn(1000000))).
		SetTLSConfig(&tls.Config{MinVersion: tls.VersionTLS12}).
		SetAutoReconnect(true)

	client := mqtt.NewClient(opts)
	if token := client.Connect(); token.Wait() && token.Error() != nil {
		panic(token.Error())
	}

	client.Subscribe(topic, 1, func(_ mqtt.Client, m mqtt.Message) {
		fmt.Printf("%s %s\n", m.Topic(), m.Payload())
	}).Wait()

	client.Publish(topic, 1, false, "hello from Go").Wait()
	time.Sleep(2 * time.Second)
	client.Disconnect(250)
}

See building a Go MQTT client for graceful shutdown and structured payloads.

Java and Kotlin: Paho and HiveMQ Client

Eclipse Paho Java ships two packages: org.eclipse.paho.client.mqttv3 for MQTT 3.1.1 and org.eclipse.paho.mqttv5.client for MQTT 5. The HiveMQ MQTT Client is an alternative with blocking, async and reactive APIs that suits Reactor or RxJava codebases. Both run on Android too, which is why Kotlin apps often use them directly.

import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import java.util.UUID;

public class LibsJava {
    public static void main(String[] args) throws Exception {
        String topic = "connectmq/demo/k3x8p1/libs/java";
        String clientId = "libs-java-" + UUID.randomUUID().toString().substring(0, 8);

        MqttClient client = new MqttClient("ssl://broker.connectmq.com:8883",
                clientId, new MemoryPersistence());
        MqttConnectOptions options = new MqttConnectOptions();
        options.setAutomaticReconnect(true);
        options.setCleanSession(true);
        client.connect(options);

        client.subscribe(topic, 1, (t, msg) ->
                System.out.println(t + " " + new String(msg.getPayload())));
        client.publish(topic, "hello from Paho Java".getBytes(), 1, false);

        Thread.sleep(2000);
        client.disconnect();
        client.close();
    }
}

Spring Boot users usually wrap this in Spring Integration; the patterns are in our Java MQTT client guide and the Android MQTT with Kotlin tutorial.

C# and .NET: MQTTnet

MQTTnet is the standard .NET library and supports MQTT 5, TLS and WebSocket. It is fully async and fits ASP.NET Core hosted services, .NET MAUI apps and Unity. Note that the major versions differ: version 5 renamed the factory to MqttClientFactory, while version 4 uses MqttFactory. The snippet targets version 5.

using MQTTnet;
using MQTTnet.Formatter;

var topic = "connectmq/demo/k3x8p1/libs/csharp";
var factory = new MqttClientFactory();
using var client = factory.CreateMqttClient();

client.ApplicationMessageReceivedAsync += e =>
{
    Console.WriteLine($"{e.ApplicationMessage.Topic} {e.ApplicationMessage.ConvertPayloadToString()}");
    return Task.CompletedTask;
};

var options = new MqttClientOptionsBuilder()
    .WithTcpServer("broker.connectmq.com", 8883)
    .WithClientId($"libs-cs-{Guid.NewGuid():N}"[..16])
    .WithProtocolVersion(MqttProtocolVersion.V500)
    .WithTlsOptions(o => o.UseTls())
    .Build();

await client.ConnectAsync(options);
await client.SubscribeAsync(factory.CreateSubscribeOptionsBuilder()
    .WithTopicFilter(topic).Build());

await client.PublishAsync(new MqttApplicationMessageBuilder()
    .WithTopic(topic).WithPayload("hello from MQTTnet").Build());

await Task.Delay(2000);
await client.DisconnectAsync();

MQTTnet does not reconnect on its own in the low-level client, so wrap the connection in a loop or a background service. The MQTTnet tutorial shows a resilient hosted-service pattern.

Rust: rumqttc

rumqttc is the most widely used Rust client. It separates the client handle from an event loop that you poll, which gives you explicit control over back-pressure. Both MQTT 3.1.1 and MQTT 5 modules exist.

use rumqttc::{AsyncClient, Event, MqttOptions, Packet, QoS};
use std::time::Duration;

#[tokio::main]
async fn main() {
    let topic = "connectmq/demo/k3x8p1/libs/rust";
    let mut opts = MqttOptions::new("libs-rust-k3x8p1", "broker.connectmq.com", 1883);
    opts.set_keep_alive(Duration::from_secs(30));

    let (client, mut eventloop) = AsyncClient::new(opts, 10);
    client.subscribe(topic, QoS::AtLeastOnce).await.unwrap();
    client.publish(topic, QoS::AtLeastOnce, false, "hello from rumqttc").await.unwrap();

    loop {
        match eventloop.poll().await {
            Ok(Event::Incoming(Packet::Publish(p))) => {
                println!("{} {}", p.topic, String::from_utf8_lossy(&p.payload));
            }
            Ok(_) => {}
            Err(e) => {
                eprintln!("connection error: {e}");
                tokio::time::sleep(Duration::from_secs(2)).await;
            }
        }
    }
}

Polling the event loop again after an error is how rumqttc reconnects. TLS needs one of its TLS feature flags; the rumqttc guide covers that setup.

C and Embedded: Paho C, libmosquitto, PubSubClient, esp-mqtt

For native Linux applications, Eclipse Paho C offers synchronous and asynchronous APIs with MQTT 5 support, and libmosquitto is a small, battle-tested alternative from the Mosquitto project.

#include <stdio.h>
#include <string.h>
#include "MQTTClient.h"

int main(void) {
    MQTTClient client;
    MQTTClient_connectOptions opts = MQTTClient_connectOptions_initializer;
    MQTTClient_message msg = MQTTClient_message_initializer;
    MQTTClient_deliveryToken token;
    const char *topic = "connectmq/demo/k3x8p1/libs/c";
    char payload[] = "hello from Paho C";

    MQTTClient_create(&client, "tcp://broker.connectmq.com:1883", "libs-c-k3x8p1",
                      MQTTCLIENT_PERSISTENCE_NONE, NULL);
    opts.keepAliveInterval = 30;
    opts.cleansession = 1;

    if (MQTTClient_connect(client, &opts) != MQTTCLIENT_SUCCESS) {
        fprintf(stderr, "connect failed\n");
        return 1;
    }

    msg.payload = payload;
    msg.payloadlen = (int)strlen(payload);
    msg.qos = 1;
    msg.retained = 0;
    MQTTClient_publishMessage(client, topic, &msg, &token);
    MQTTClient_waitForCompletion(client, token, 10000L);

    MQTTClient_disconnect(client, 10000);
    MQTTClient_destroy(&client);
    return 0;
}

On microcontrollers the trade-offs change. PubSubClient is tiny and simple but supports only MQTT 3.1.1, publishes at QoS 0 and has a default packet buffer of 256 bytes that you must raise with setBufferSize() for JSON payloads. AsyncMqttClient adds QoS 1 and 2 publishing with a non-blocking design. For production ESP32 firmware, Espressif's esp-mqtt component in ESP-IDF supports MQTT 5, TLS and WebSocket and is maintained by the chip vendor. Start with the ESP32 MQTT tutorial for working Arduino code, and with Paho MQTT C for Linux devices.

Mobile: Swift, Kotlin, Flutter and React Native

Mobile clients face a constraint servers do not: the operating system suspends background apps and kills idle sockets. Whatever library you pick, plan for frequent reconnects, use persistent sessions or retained messages to catch up, and use push notifications for anything that must reach a sleeping phone.

  • iOS / macOS: CocoaMQTT supports MQTT 5, TLS and WebSocket. See MQTT on iOS with Swift.
  • Android: Paho Java or the HiveMQ client from Kotlin, ideally inside a lifecycle-aware component.
  • Flutter: mqtt_client covers server and browser targets from one Dart codebase. See the Flutter MQTT tutorial.
  • React Native: MQTT.js over WebSocket is the common route, with a few polyfills. See React Native MQTT.

Pitfalls That Look Like Library Bugs

Most issues blamed on a library are actually configuration or design problems. Check these first:

Symptom Real cause Fix
Constant connect/disconnect loop Two processes share one client ID Generate unique IDs; the broker drops the older session
No messages after reconnect Clean session plus no resubscribe Resubscribe in the connect callback, not once at startup
Event loop freezes Blocking work inside a message callback Hand work to a queue, thread pool or async task
Large payloads silently dropped Client buffer or broker size limit Raise the client buffer; respect broker limits
TLS handshake fails on device Missing root CA or wrong clock Install the CA bundle and sync time via NTP before connecting
Duplicates at QoS 1 Expected at-least-once behavior Make handlers idempotent; see MQTT QoS levels explained

Reconnect handling deserves special attention in Python; the paho-mqtt reconnect guide explains the backoff settings. For a systematic approach to verifying a client before shipping, read testing MQTT clients, and keep mosquitto_pub and mosquitto_sub examples handy as an independent second client when debugging.

Moving From the Public Broker to Your Own

Every snippet above uses the anonymous public broker, which is perfect for comparing libraries side by side: run the Python subscriber, publish from Go, watch the message arrive. The public MQTT test broker is shared and readable by anyone, though, so production clients need credentials and an isolated broker.

ConnectMQ is a fully managed MQTT service that works with every library in this guide, because it speaks standard MQTT 3.1.1 and 5.0 over TCP 1883, TLS 8883, WebSocket 8083 and Secure WebSocket 8084 (path /mqtt). When you create a broker in the portal you receive a username, password and client ID. Switching any snippet above is a three-line change, shown here for paho-mqtt:

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="YOUR_CLIENT_ID", protocol=mqtt.MQTTv5)
client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.tls_set()

ConnectMQ binds client IDs to issued credentials, so use the generated ID exactly as given. If you are still deciding how to host your broker, our guide to the cloud MQTT broker landscape compares serverless, dedicated and bring-your-own-cloud options.

Connect Any MQTT Library to ConnectMQ

Pick the library that matches your stack, run its snippet against broker.connectmq.com, and when it is time for private topics, create a free broker at app.connectmq.com. The Serverless free plan needs no credit card and gives you up to 25 connections, QoS up to 1 and 256 KB messages, which is plenty for building and testing clients in several languages at once.

Frequently Asked Questions

Which MQTT client library is the best?

There is no single best one; the right choice is the maintained library that matches your language and concurrency model. paho-mqtt for Python, MQTT.js for JavaScript, MQTTnet for .NET and Paho or rumqttc for Go and Rust are the common, well-supported defaults.

Do all MQTT client libraries support MQTT 5?

No. Several popular ones, including PubSubClient for Arduino and the classic Paho Go client, only implement MQTT 3.1.1. If you need shared subscriptions, message expiry or reason codes, confirm MQTT 5 support before you adopt a library.

Can clients written in different languages talk to each other?

Yes. The broker routes bytes, not language objects, so a Python publisher and a C# subscriber interoperate as long as they agree on topics and payload format. JSON is the easiest common format to start with.

Which library should I use in the browser?

MQTT.js over Secure WebSocket is the standard choice. Connect to wss://broker.connectmq.com:8084/mqtt, because browsers cannot open raw TCP sockets and HTTPS pages block insecure WebSocket connections.

Why does my client work on the public broker but not on a private one?

Usually it is missing credentials, a client ID that does not match the issued one, or TLS not being enabled. Check the CONNACK reason code your library reports; it tells you exactly which of these is wrong.

Related articles

Best MQTT CLI Tools for Testing, Scripting and Debugging

A hands-on comparison of mosquitto_pub/sub, MQTTX CLI, HiveMQ MQTT CLI, the MQTT.js CLI, curl and mqttui, with the same test run against broker.connectmq.com so you can choose the right tool for scripting and debugging.

8 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.