2023-07-15 22:54:13 +01:00
---
title: "Build React DApps Using @waku/react "
2023-11-23 12:01:32 +01:00
hide_table_of_contents: true
2025-09-29 16:34:46 +10:00
displayed_sidebar: build
2023-07-15 22:54:13 +01:00
---
2023-12-08 01:34:49 -08:00
:::caution
Currently, the JavaScript Waku SDK (`@waku/sdk` ) is **NOT compatible** with React Native. We plan to add support for React Native in the future.
:::
2023-07-27 13:50:30 +01:00
The [@waku/react ](https://www.npmjs.com/package/@waku/react ) package provides components and UI adapters to integrate `@waku/sdk` into React applications effortlessly. This guide provides detailed steps for using `@waku/react` in your project.
2023-07-15 22:54:13 +01:00
2023-11-23 12:01:32 +01:00
## Install the dependencies
2023-07-15 22:54:13 +01:00
2023-11-22 23:28:54 +01:00
First, set up a project using any [production-grade React framework ](https://react.dev/learn/start-a-new-react-project ) or an existing React application. For this guide, we will create a boilerplate using [ViteJS ](https://vitejs.dev/guide/ ):
2023-07-15 22:54:13 +01:00
```mdx-code-block
import Tabs from '@theme/Tabs ';
import TabItem from '@theme/TabItem ';
```
< Tabs groupId = "package-manager" >
2023-11-22 23:28:54 +01:00
< TabItem value = "npm" label = "NPM" >
2023-07-15 22:54:13 +01:00
```shell
2023-11-22 23:28:54 +01:00
npm create vite@latest [PROJECT DIRECTORY] -- --template react
2023-07-15 22:54:13 +01:00
```
< / TabItem >
< TabItem value = "yarn" label = "Yarn" >
```shell
2023-11-22 23:28:54 +01:00
yarn create vite [PROJECT DIRECTORY] --template react
2023-07-15 22:54:13 +01:00
```
< / TabItem >
< / Tabs >
2024-01-11 10:42:56 +01:00
Next, install the required packages for integrating `@waku/sdk` using your preferred package manager:
2023-07-15 22:54:13 +01:00
< Tabs groupId = "package-manager" >
2023-11-22 23:28:54 +01:00
< TabItem value = "npm" label = "NPM" >
2023-07-15 22:54:13 +01:00
```shell
2023-11-22 23:28:54 +01:00
npm install @waku/react @waku/sdk protobufjs
2023-07-15 22:54:13 +01:00
```
< / TabItem >
< TabItem value = "yarn" label = "Yarn" >
```shell
2023-11-22 23:28:54 +01:00
yarn add @waku/react @waku/sdk protobufjs
2023-07-15 22:54:13 +01:00
```
< / TabItem >
< / Tabs >
2023-11-23 12:01:32 +01:00
## Initialise the Waku provider
2023-07-15 22:54:13 +01:00
2023-11-22 23:28:54 +01:00
In the `main.jsx` file, which serves as the entry point for a React app, we will set up the `LightNodeProvider` [context provider ](https://react.dev/reference/react/createContext#provider ) to wrap the entire application within the Waku provider. Import the following on top of your file:
2023-07-15 22:54:13 +01:00
2023-11-22 23:28:54 +01:00
```js title="src/main.jsx"
import { LightNodeProvider } from "@waku/react ";
// Set the Light Node options
const NODE_OPTIONS = { defaultBootstrap: true };
ReactDOM.createRoot(document.getElementById('root')).render(
// Use the Light Node context provider
< React.StrictMode >
< LightNodeProvider options = {NODE_OPTIONS} >
< App / >
< / LightNodeProvider >
< / React.StrictMode > ,
)
```
2023-11-23 12:01:32 +01:00
Next, create and start a [Light Node ](/learn/glossary#light-node ) using the `useWaku()` function within the `App.jsx` file:
2023-11-22 23:28:54 +01:00
```js title="src/App.jsx"
import { useWaku } from "@waku/react ";
2023-07-15 22:54:13 +01:00
function App() {
2023-11-22 23:28:54 +01:00
// Create and start a Light Node
const { node, error, isLoading } = useWaku();
2023-07-15 22:54:13 +01:00
// "node" is the created Light Node
// "error" captures any error that occurs during node creation
// "isLoading" indicates whether the node is still being created
}
```
2023-11-23 12:01:32 +01:00
## Build the application interface
2023-07-15 22:54:13 +01:00
2023-11-22 23:28:54 +01:00
Let's build a user interface for sending messages and viewing past messages, modify the `App.jsx` file with the following code block:
2023-07-15 22:54:13 +01:00
2023-11-22 23:28:54 +01:00
```js title="src/App.jsx"
import { useState, useEffect } from 'react';
2023-07-18 19:51:12 +01:00
import { useWaku } from "@waku/react ";
2023-11-22 23:28:54 +01:00
import { createEncoder, createDecoder } from "@waku/sdk ";
import protobuf from 'protobufjs';
import './App.css'
2023-07-15 22:54:13 +01:00
2023-07-18 19:51:12 +01:00
function App() {
2023-11-22 23:28:54 +01:00
const [inputMessage, setInputMessage] = useState("");
const [messages, setMessages] = useState([]);
// Update the inputMessage state as the user input changes
const handleInputChange = (e) => {
setInputMessage(e.target.value);
};
2023-07-18 19:51:12 +01:00
// Create and start a Light Node
const { node, error, isLoading } = useWaku();
2023-11-22 23:28:54 +01:00
// Create a message encoder and decoder
const contentTopic = "/waku-react-guide/1/chat/proto";
const encoder = createEncoder({ contentTopic });
const decoder = createDecoder(contentTopic);
// Create a message structure using Protobuf
2024-10-01 07:53:34 +01:00
const DataPacket = new protobuf.Type("DataPacket")
2023-11-22 23:28:54 +01:00
.add(new protobuf.Field("timestamp", 1, "uint64"))
.add(new protobuf.Field("message", 2, "string"));
// Send the message using Light Push
const sendMessage = async () => {}
return (
< >
< div className = "chat-interface" >
< h1 > Waku React Demo< / h1 >
< div className = "chat-body" >
{messages.map((message, index) => (
< div key = {index} className = "chat-message" >
< span > {new Date(message.timestamp).toUTCString()}< / span >
< div className = "message-text" > {message.message}< / div >
< / div >
))}
< / div >
< div className = "chat-footer" >
< input
type="text"
id="message-input"
value={inputMessage}
onChange={handleInputChange}
placeholder="Type your message..."
/>
< button className = "send-button" onClick = {sendMessage} > Send< / button >
< / div >
< / div >
< />
)
2023-07-15 22:54:13 +01:00
}
2023-11-22 23:28:54 +01:00
export default App
2023-07-15 22:54:13 +01:00
```
2023-11-22 23:28:54 +01:00
:::info
2025-09-29 23:18:55 +10:00
In the code above, we also created a message `encoder` and `decoder` using the `createEncoder()` and `createDecoder()` functions, along with the application [message structure ](/build/javascript/#message-structure ) with Protobuf.
2023-11-22 23:28:54 +01:00
:::
2023-07-15 22:54:13 +01:00
2023-11-22 23:28:54 +01:00
Next, modify the `App.css` file with the following code block:
2023-07-15 22:54:13 +01:00
2023-11-22 23:28:54 +01:00
```css title="src/App.css"
#root {
margin: 0 auto;
}
2023-07-15 22:54:13 +01:00
2023-11-22 23:28:54 +01:00
.chat-interface {
display: flex;
flex-direction: column;
height: 100vh;
border: 1px solid #ccc ;
}
2023-07-15 22:54:13 +01:00
2023-11-22 23:28:54 +01:00
.chat-body {
flex-grow: 1;
overflow-y: auto;
padding: 10px;
}
.message-text {
background-color: #f1f1f1 ;
color: #000 ;
padding: 10px;
margin-bottom: 10px;
}
.chat-footer {
display: flex;
padding: 10px;
background-color: #f1f1f1 ;
align-items: center;
}
2023-07-15 22:54:13 +01:00
2023-11-22 23:28:54 +01:00
#message-input {
flex-grow: 1;
border-radius: 4px;
padding: 10px;
margin-right: 10px;
}
.send-button {
background-color: #007bff ;
border-radius: 4px;
}
```
2023-11-23 12:01:32 +01:00
## Send messages using light push
2023-11-22 23:28:54 +01:00
2025-09-29 23:18:55 +10:00
To send messages in our application, we need to modify the `sendMessage()` function to serialize user input into our Protobuf structure and [push it to the network ](/build/javascript/light-send-receive#send-messages-using-light-push ) using the `useLightPush()` function:
2023-11-22 23:28:54 +01:00
```js title="src/App.jsx"
import { useLightPush } from "@waku/react ";
function App() {
2023-07-15 22:54:13 +01:00
// Bind push method to a node and encoder
const { push } = useLightPush({ node, encoder });
// Send the message using Light Push
2023-11-22 23:28:54 +01:00
const sendMessage = async () => {
if (!push || inputMessage.length === 0) return;
// Create a new message object
const timestamp = Date.now();
2024-10-01 07:53:34 +01:00
const protoMessage = DataPacket.create({
2023-11-22 23:28:54 +01:00
timestamp: timestamp,
message: inputMessage
});
// Serialise the message and push to the network
2024-10-01 07:53:34 +01:00
const payload = DataPacket.encode(protoMessage).finish();
2023-11-22 23:28:54 +01:00
const { recipients, errors } = await push({ payload, timestamp });
// Check for errors
if (errors.length === 0) {
setInputMessage("");
console.log("MESSAGE PUSHED");
} else {
console.log(errors);
2023-07-15 22:54:13 +01:00
}
};
}
```
2023-11-23 12:01:32 +01:00
## Receive messages using filter
2023-07-15 22:54:13 +01:00
2025-09-29 23:18:55 +10:00
To display messages in our application, we need to use the `useFilterMessages()` function to create a [Filter subscription ](/build/javascript/light-send-receive/#receive-messages-using-filter ), receive incoming messages, and render them in our interface:
2023-07-15 22:54:13 +01:00
2023-11-22 23:28:54 +01:00
```js title="src/App.jsx"
import { useFilterMessages } from "@waku/react ";
2023-07-15 22:54:13 +01:00
function App() {
// Receive messages from Filter subscription
2023-11-22 23:28:54 +01:00
const { messages: filterMessages } = useFilterMessages({ node, decoder });
// Render the list of messages
useEffect(() => {
setMessages(filterMessages.map((wakuMessage) => {
if (!wakuMessage.payload) return;
2024-10-01 07:53:34 +01:00
return DataPacket.decode(wakuMessage.payload);
2023-11-22 23:28:54 +01:00
}));
}, [filterMessages]);
2023-07-15 22:54:13 +01:00
}
```
2023-11-23 12:01:32 +01:00
## Retrieve messages using store
2023-07-15 22:54:13 +01:00
2025-09-29 23:18:55 +10:00
To display messages from the past, we need to retrieve them from the [Store protocol ](/build/javascript/store-retrieve-messages ) using the `useStoreMessages()` function when our application initialises and then render them alongside newly received messages:
2023-07-15 22:54:13 +01:00
2023-11-22 23:28:54 +01:00
```js title="src/App.jsx"
import { useFilterMessages, useStoreMessages } from "@waku/react ";
2023-07-15 22:54:13 +01:00
function App() {
2023-11-22 23:28:54 +01:00
// Query Store peers for past messages
const { messages: storeMessages } = useStoreMessages({ node, decoder });
2023-07-15 22:54:13 +01:00
2023-11-22 23:28:54 +01:00
// Receive messages from Filter subscription
const { messages: filterMessages } = useFilterMessages({ node, decoder });
// Render both past and new messages
useEffect(() => {
const allMessages = storeMessages.concat(filterMessages);
setMessages(allMessages.map((wakuMessage) => {
if (!wakuMessage.payload) return;
2024-10-01 07:53:34 +01:00
return DataPacket.decode(wakuMessage.payload);
2023-11-22 23:28:54 +01:00
}));
}, [filterMessages, storeMessages]);
2023-07-15 22:54:13 +01:00
}
```
2025-01-31 17:41:09 +05:30
You can also configure a specific Store peer when creating the node, which is useful when running your own Store node or using a specific node in the network:
```js
const node = await createLightNode({
defaultBootstrap: true,
store: {
peer: "/ip4/1.2.3.4/tcp/1234/p2p/16Uiu2HAm..." // multiaddr or PeerId of your Store node
}
});
```
If the specified Store peer is not available, the node will fall back to using random Store peers in the network.
2023-07-15 22:54:13 +01:00
:::info
2025-09-29 23:18:55 +10:00
To explore the available Store query options, have a look at the [Retrieve Messages Using Store Protocol ](/build/javascript/store-retrieve-messages#store-query-options ) guide.
2023-07-15 22:54:13 +01:00
:::
:::tip
2024-10-29 09:09:25 +05:30
You have successfully integrated `@waku/sdk` into a React application using the `@waku/react` package. Have a look at the [web-chat ](https://github.com/waku-org/js-waku-examples/tree/master/examples/web-chat ) example for a working demo and the [Building a Tic-Tac-Toe Game with Waku ](https://blog.waku.org/2024-01-22-tictactoe-tutorial/ ) tutorial to learn more.
2024-10-01 07:53:34 +01:00
:::