Our first chat library

One of the interesting things about building Plain is that we get to help two groups of customers at the same time: our customers – the companies using Plain – and our customers' customers: the people getting in touch with a question or an issue they'd like resolved.

After many months of focusing on the experience for the former, we've recently started focusing on the experience for the latter – beginning with our chat experience. Last week, we achieved an important milestone here with the completion of our chat library, which we'll cover in more depth in this post.

The way most existing customer service chat solutions work is that you often trade a quick setup for security or engineering tradeoffs. You are often forced to load a third-party script which has some engineering tradeoffs:

  • They are not auditable and can change without any notice
  • They are hard to control when they load, especially in declarative UI frameworks such as React, where it can be hard to control the lifecycle of a "global" embed.
  • You end up with an experience disconnected from the rest of your product: more often than not, you end up with a Clippy-style floating bubble whose appearance, behaviour and components are hard to control.

As a customer, on the other hand, you often end up getting a chat experience that feels ephemeral – if you close the tab, the conversation and its contents are gone. Sometimes, you get to control the beginning and end of a conversation. But this approach causes more problems than it solves, like customers talking to two different advisors in two different conversations about the same issue.

The way we're building chat at Plain is designed to solve these problems.

  • Firstly, we're developing our embeddable chat UI in the open. This means that we will have public repositories for our chat UI which everyone can read, inspect and, if they wish, fork! We're still doing some work on all the necessary harnesses, testing and so forth that you'd expect of a good open source project, but this is our goal. For alpha access, get in touch at mattandsimon@plain.com.
  • Second, we're building for near-complete control over the chat interface itself. We think the best customer service is treated like a feature - part of your USP and seamlessly embedded into your product. To do this, we're developing UI packages you can use directly instead of having to rely on script tags. Given we're building everything internally in React, the first package we are supporting is a React-based npm package.
  • Third, we're building our chat experience on top of a simple but powerful data model. A persisted, ongoing conversation between customer and advisor with no distractions in between - no conversations, tickets or other abstractions. We think great customer service makes things simple for the customer while managing complexity and nuance gracefully behind the scenes.

We are kicking off with support for the following features:

  • Simple and secure verification of logged in customers via a signed JWT you provide
  • Timeline component which renders the timeline for the customer and allows them to chat with the advisor
  • usePlain() hook to show unread counts in your own UI/navigation
  • Typescript types (of course!)
  • And, excitingly, full theming support for nearly everything and anything
Examples of our chat theming
Examples of our chat theming

The next pieces of work we want to tackle around authenticating logged out customers and verifying their details securely, helping customers transition smoothly from chat to email and back, and expectation setting for customers so they know when they will be helped. We're really excited about the many foundational improvements we'll be making here over the coming months.


  • 🎨 We've given our workspace picker page, 404 pages, sign-up form, workspace creation page and global error page a lick of paint
  • 🛑 We've significantly improved our error handling throughout the support app so that everything errors consistently when something goes wrong
  • 🔐 We've implemented a more granular permission structure that should make sense for most teams. We now also allow users to any combination of the following roles:
    • Admin: Full access to everything including deleting the workspace.
    • Help customers: Can view, assign and communicate with customers. Can open issues, leave notes and change customer statuses.
    • Manage content settings: Can create, edit and remove issue types and snippets.
    • Manage developer settings: Can create, edit and remove workspace apps.
    • Manage workspace members: Can view, assign and communicate with customers. Can view workspace and content settings.
  • 🥞 Many small breaking API schema changes for greater consistency and to prevent further breaking changes later.

Bug fixes

  • Fixed a bug which resulted in workspace invites being deleted sometimes erroring
  • Fixed a bug which resulted in issue resolution entries shortly appearing out of order in the timeline before they flipped back to their actual order (Optimistic updates are hard it turns out).
  • When you log out you are now redirected to the login page (rather than a blank page with a slightly ominous "bye bye" written on it)
  • Workspace invitations now show up in the list of invites immediately rather than after refresh
  • #changelog
© Plain. CS without the BS since 2020.