NextJS & Twilio Conversations SDK issue

I’ve been building a nextJS app and the final feature I need to add is to is allow conversational chat from the app’s users to SMS using the twilio conversations SDK.

I can interact with the SDK fully as expected in server components (create, join, load conversations, load the messages within those conversations, ect) but I need access to state to pull in new messages without the user having to refresh the page. Problem is that as soon as I try to do anything in a client component I run into trouble.

The objects from the SDK look something like this:

Message {
    _events: [Object: null prototype] { updated: [Function (anonymous)] },
    _eventsCount: 1,
    _maxListeners: undefined,
    eventHistory: Map(0) {},
    conversation: Conversation {
      _events: [Object: null prototype],
      _eventsCount: 10,
      _maxListeners: undefined,
      eventHistory: [Map],
      sid: 'AN ID',
      _links: [Object],
      _configuration: [Configuration],
      _services: [ClientServices],
      _entityName: 'AN ID',
      _internalState: [Object],
      _participants: [Map],
      _participantsEntity: [Participants],
      _messagesEntity: [Messages],
      _entityPromise: [Promise],
      _entity: [SyncDocument],
      _messagesList: [SyncList],
      _participantsMap: [SyncMap],
      _dataSource: 'rest',
      [Symbol(shapeMode)]: false,
      [Symbol(kCapture)]: false
    },
    configuration: Configuration {
      typingIndicatorTimeoutDefault: 5000,
      productId: 'ip_messaging',
      links: [Object],
      limits: [Object],
      typingIndicatorTimeoutOverride: undefined,
      backoffConfiguration: [Object],
      retryWhenThrottled: true,
      userInfosToSubscribe: 100,
      reachabilityEnabled: false,
      userIdentity: 'EMAIL',
      userInfo: 'AN ID',
      myConversations: 'EMAIL',
      httpCacheInterval: 1,
      consumptionReportInterval: 1,
      channelMetadataCacheCapacity: 100,
      messageRecipientsCacheCapacity: 1000
    },
    services: ClientServices {
      twilsockClient: [_class],
      transport: [_class],
      notificationClient: [_class],
      syncClient: [Client],
      commandExecutor: [CommandExecutor],
      contentClient: [ContentClient],
      channelMetadataClient: [ChannelMetadataClient],
      messageRecipientsClient: [MessageRecipientsClient],
      typingIndicator: [TypingIndicator],
      network: [Network],
      users: [Users],
      mcsClient: [_class]
    },
    state: {
      sid: 'AN ID',
      index: 21,
      author: 'PHONE NUMBER',
      subject: null,
      contentSid: null,
      body: 'Test message',
      timestamp: 2024-11-21T14:24:32.545Z,
      dateUpdated: 2024-11-21T14:24:32.545Z,
      lastUpdatedBy: null,
      attributes: {},
      type: 'text',
      media: null,
      medias: null,
      participantSid: 'AN ID',
      aggregatedDeliveryReceipt: null,
      hasChannelMetadata: false
    },
    [Symbol(shapeMode)]: false,
    [Symbol(kCapture)]: false
  }

Twilios react example simply passes these objects as props to the the “conversation” component without issue. But when I try to pass this object from a server component to a client component in NextJS I get “Error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.”

Tried using JSON.stringify before passing as props but it can’t be converted to JSON because “TypeError: Converting circular structure to JSON”.

I tried returning the SDK object from the route but next/response sends it as JSON so I run into the same issue with the “TypeError: Converting circular structure to JSON”.

I’ve tried getting the data from the SDK from within the client component but I can’t get it to do that either. I seem to only be able to do anything with the SDK exclusively in server components.

So far the closest I’ve gotten is to send a request to a route, which returns an array of the messages but then it’s just an array… I can’t interact with the SDK object to listen for new messages, get read receipts, ect.

I’m new to all this, learned quickly and have accomplished a lot in my app until this point. I’m sure there’s a simple answer but I need a shove in the right direction.

Thanks in advance!

Hey @pjgorton. The object you shared makes me think you might have an promise that you need to await. Something like this:

const response = await fetch('/api/send-message', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ message, to: phoneNumber }),
})

const data = await response.json()

I don’t know much about the Twilio Conversations JavaScript SDK, so I asked v0 to build a minimal example in case it helps.

I know this is all fairly generic advice. If you can share code snippets to show the function and how state is being used, someone here might be able to recognize the specific issue.

Thank you for the reply. Very much appreciated.

I looked at the way it’s generating the twilio client from within a client component and when I tried it on my app… it worked!

I’m positive I tried doing the exact same thing prior but I must have missed something.

I had moved on from this, gave up and just setup the client components to poll the server every 15seconds with tanstack query… which is working but not ideal. Will definitely circle back and rebuild.

Thanks again.

2 Likes

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.