diff --git a/docs/react-migration-guide.md b/docs/react-migration-guide.md
index 6bc1705c2..e8f8bcea3 100644
--- a/docs/react-migration-guide.md
+++ b/docs/react-migration-guide.md
@@ -1,5 +1,207 @@
# React hooks upgrade / migration guide
+## Version ?? to ??
+
+### ChannelProvider
+
+In previous versions, you were able to provide channel options as a parameter in the `useChannel`/`usePresence` hooks. This often led to errors when using hooks with the same channel name but different options, or when attempting to dynamically change options for a channel.
+
+To address these issues, the new version introduces the `ChannelProvider` component to define the channels you wish to use and their options. The ability to provide channel options using the `options` or `deriveOptions` parameters in `useChannel`/`usePresence` hooks has been removed.
+
+Replace code that used `options` in the `useChannel` hook:
+
+```jsx
+const { channel } = useChannel(
+ { channelName: 'your-channel-name', options: { params: { rewind: '1' } } },
+ (message) => {
+ console.log(message);
+ },
+);
+```
+
+With this:
+
+```jsx
+// in a parent component:
+return
+ {children}
+
+
+// in a child component:
+const { channel } = useChannel({ channelName: 'your-channel-name' }, (message) => {
+ console.log(message);
+});
+```
+
+Replace code that used `deriveOptions` in `useChannel` hook:
+
+```jsx
+useChannel(
+ {
+ channelName: 'your-derived-channel-name',
+ deriveOptions: { filter: 'headers.role == `"marketing"`' },
+ },
+ (message) => {
+ console.log(message);
+ },
+);
+```
+
+With this:
+
+```jsx
+// in a parent component:
+return
+ {children}
+
+
+// in a child component:
+useChannel({ channelName: 'your-derived-channel-name' }, (message) => {
+ console.log(message);
+});
+```
+
+Replace code that used `options` in `usePresence` hook:
+
+```jsx
+const { updateStatus } = usePresence(
+ { channelName: 'presence-channel-name', options: { modes: ['PRESENCE'] } },
+ { foo: 'bar' },
+);
+```
+
+With this:
+
+```jsx
+// in a parent component:
+return
+ {children}
+
+
+// in a child component:
+const { updateStatus } = usePresence({ channelName: 'presence-channel-name' }, { foo: 'bar' });
+```
+
+Additionally, if you were calling `.setOptions()` on a channel instance returned by the `useChannel` hook before, you must remove those calls and instead modify options provided to the `ChannelProvider` component if you want to change channel options during runtime.
+
+Change this:
+
+```jsx
+const { channel } = useChannel({ channelName: 'your-channel-name' }, (message) => {
+ console.log(message);
+});
+channel.setOptions({ params: { rewind: '1' } });
+```
+
+To this:
+
+```jsx
+// in a parent component:
+// change channel options during runtime using state
+const [options, setOptions] = useState({});
+return
+ {children}
+
+
+// in a child component:
+const { channel } = useChannel({ channelName: 'your-channel-name' }, (message) => {
+ console.log(message);
+});
+```
+
+### `usePresence` is split into two hooks
+
+The functionality of the `usePresence` hook has been split into two separate hooks.
+
+The `usePresence` hook can now only be used to enter the presence with optional initial state and update the presence status for the current client. It no longer returns the `presenceData` value and does not accept the `onPresenceUpdated` callback as its third parameter.
+
+To listen for presence updates, a new hook called `usePresenceListener` has been introduced. This hook returns the `presenceData` object previously returned by `usePresence` and accepts an `onPresenceMessageReceived` callback as its second parameter, which is called on new presence messages.
+
+Replace this:
+
+```jsx
+const { presenceData, updateStatus } = usePresence(
+ { channelName: 'presence-channel-name' },
+ { foo: 'bar' },
+ (update) => {
+ console.log(update);
+ },
+);
+```
+
+With this:
+
+```jsx
+const { updateStatus } = usePresence({ channelName: 'presence-channel-name' }, { foo: 'bar' });
+const { presenceData } = usePresenceListener({ channelName: 'presence-channel-name' }, (update) => {
+ console.log(update);
+});
+```
+
+### Renaming `id` to `ablyId`
+
+All instances of the `id` field, which were used to specify the `AblyProvider` component and the underlying Ably client to use, have been renamed to `ablyId`.
+
+Replace this:
+
+```jsx
+// in a parent component:
+const client = new Ably.Realtime(options);
+
+return
+ {children}
+
+
+// in a child component:
+useChannel({ channelName: 'your-channel-name', id: 'foo' }, (message) => {
+ console.log(message);
+});
+```
+
+With this:
+
+```jsx
+// in a parent component:
+const client = new Ably.Realtime(options);
+
+return
+
+ {children}
+
+
+
+// in a child component:
+useChannel({ channelName: 'your-channel-name', ablyId: 'foo' }, (message) => {
+ console.log(message);
+});
+```
+
+### New convenience function `publish` in `useChannel`
+
+A new convenience function, `publish`, is now being returned by the `useChannel` hook. It performs the same function as calling `channel.publish()`. Additionally, using this dedicated `publish` function allows you to send messages to derived channels (channels with a filter qualifier) without attaching to the channel or using other workarounds.
+
+It is recommended to use the dedicated `publish` function returned by the `useChannel` hook instead of calling `channel.publish()`.
+
+Replace this:
+
+```jsx
+const { channel } = useChannel({ channelName: 'your-channel-name' });
+
+channel.publish('test-message', {
+ text: 'message text',
+});
+```
+
+With this:
+
+```jsx
+const { publish } = useChannel({ channelName: 'your-channel-name' });
+
+publish('test-message', {
+ text: 'message text',
+});
+```
+
## Version 2.x to 3.x
### Hooks now return object
@@ -14,7 +216,7 @@ const { channel, ably } = useChannel("your-channel-name", (message) => { /* ...
const { presenceData, updateStatus } = usePresence("your-channel-name");
```
-### Replacing `configureAbly` with `AblyProvider`
+### Replacing `configureAbly` with `AblyProvider`
In versions 1 and 2 of our react-hooks, we exported a function called `configureAbly` which was used to register an Ably client instance to global state.
This caused a few issues (most notably it made the hooks difficult to use with hot module reloading), so we have replaced the global configuration function with a context provider (`AblyProvider`)