Thumbnail image for changelog item

Carry over in-progress users beta

The new “Carry over in-progress users” transition enables you to seamlessly transition users and their state to a new flow version without having the user restart or exit the flow, so you can iterate on flows without users losing progress. For example, if you have 150 users on a checklist version who have completed different amounts of items, you could create a new checklist version with new items and transition the in-progress users to the new version. If a user's state can't be carried over due to incompatible changes like loops in the flow, those users will be restarted from the beginning of the flow. This feature is currently in beta — we’d love your feedback!

We also renamed the other transition methods and redesigned the transition modal to be much more clear.

Read the docs →

Enhancements to built-in analytics

We’ve upgraded our in-app analytics capabilities to better encapsulate funnel analysis. Only users who have started in the specified period will be counted towards the finished and stopped statistics.

Flow analytics page

We’ve also included percentage markers and trend indicators to show how many users out of the ones that started the flow have finished or stopped and if that number has increased or decreased since the previous period.

Improvements to how we build our open source packages

Our open source packages previously produced build artifacts that were intended to be friendly to various build and module systems by using "type": "commonjs" in their package.json (the default and less modern configuration) and by using file extensions to delineate between module systems (e.g. mjs for ESM and cjs for CJS). This strategy caused issues for older build tools that do not support these file extensions.

We’ve moved all of our open source packages and their transitive "dependencies" to use @dopt/pkg-build, our newly created CLI for building packages in the most backward compatible way we can come up with.

This strategy shifts us to:

  1. Use "type": "module" in our package.json
  2. Output files with .js extensions
  3. Produce a ./cjs directory in the build output that contains a thin package.json, which configures "type": "commonjs" explicitly, conceptually overriding the "type": "module" configuration of the actual package.json.

Improvements to component types

We now export the component hook return type definition to make it easier to type the custom components you build on top of our component hooks. Here’s an example of using them to define props in a custom checklist:

import { type ChecklistItem } from '@dopt/react-checklist/hooks';
interface CustomChecklistItemProps {  completed: ChecklistItem['completed'];  title: ChecklistItem['title'];  body: ChecklistItem['body'];}

Other improvements

  • Our new HTML rich text render enables you to use Dopt’s rich text fields in all non-React UIs you’re building. Read the docs →
  • You can no longer reset the flow state when the flow is disabled.

Thumbnail image for changelog item

Announcing in-app announcements

We’ve added support for making in-app announcements without code changes! First, use Dopt to develop an in-app modal, card, fly-out (or anything) and then launch announcements through it without a dev.

This enables you to announce new products or features, communicate changes, notify users, and promote events in your product.

To deploy self-serve in-app announcements, you use the new Restart all users version transition combined with the latest version tag in your code.

Read more at our blog here →

Read the guide on launching in-app announcements in our docs →

New in-app getting started tutorial

Our new getting started tutorial helps you learn the basics of Dopt by creating an example app that uses our new pre-built components and rich text content. The example flow is now created on demand rather than automatically, helping to keep your workspace tidy.

You can access the in-app getting started tutorial via the main nav.

See a live version of the example app you create in the tutorial →

Easier user and group identification in React

We shipped @dopt/react-users, a React-specific utility for identifying users and groups to Dopt. The goal was to make the identification of users and initialization of our React SDK dead simple. See a typical usage in our example code here.

The packages provides a DoptUsersProvider that you initialize with your User API key. Here’s an example:

<DoptUsersProvider apiKey={DOPT_USERS_API_KEY}>  <App /></DoptUsersProvider>

Inside the <App /> from above, you can now use the package’s hooks, identifyUser and identifyGroup. Here’s an example of identifyUser:

export function App() {  // Identify an example user to Dopt the first time the App loads.  const userId = useIdentifyUser({    identifier: nanoid(),    properties: {      company: 'Dopt',      role: 'admin',      inTrial: true,    },  });
  return (    <DoptProvider      apiKey={DOPT_BLOCKS_API_KEY}      userId={userId}      flowVersions={{ 'custom-card-component': 1 }}    >      <Home />    </DoptProvider>  );}

The hook will correctly return undefined for the userId until the request has been made successfully, which allows the <DoptProvider /> to initialize correctly!

We've been using this wrapper package all over the place in our own examples and internal tests and found it super useful, so excited to ship it for all of you.

Read the docs →

In-app feedback

You can now give us feedback from in the product! You can find the feedback form in the main nav. We look forward to hearing from you 😄.

Other improvements

  • We’ve defaulted path names to complete making it easier to create paths from custom blocks. You can still rename the path to whatever you want!
  • We now only allow one menu and popover to be opened at time, simplifying the UI.
  • We fixed a bug where we weren’t redirecting users to the page they were trying to access if they happened to be logged out. You’ll now be redirected to the right page.

Thumbnail image for changelog item

Card component

We launched a new pre-built card component.

Cards are simple but effective. And they’re super flexible. They can be used as contextual tips to teach concepts so users know how to use and get value from your product. They can also be used as an entry point to the next step, like giving them an opportunity to opt-in to a product tour. Or they can bring external content like docs and support into the product at the right time and place, powering a PLG motion.

Here’s an example usage of the card component. In code, you import the component and access the content and state of the block with a hook.

import Card, { useCard } from '@dopt/react-card';
function MyCard() {  const card = useCard('my-flow.four-pandas-jam');
  return (    <Card.Root active={}>      <Card.Content>        <Card.Header>          <Card.Title>{card.title}</Card.Title>          <Card.DismissIcon onClick={card.dismiss} />        </Card.Header>        <Card.Body>{card.body}</Card.Body>        <Card.Footer>          <Card.DismissButton onClick={card.dismiss}>            {card.dismissLabel}          </Card.DismissButton>          <Card.CompleteButton onClick={card.complete}>            {card.completeLabel}          </Card.CompleteButton>        </Card.Footer>      </Card.Content>    </Card.Root>  );}

Our components are fully composable and give you easy access to all children for customization, such as adding your own event handlers, composing with your own components, or removing children you don’t need.

You can use the components out of the box as a pre-built component or break out and use it headlessly with your own UI component.

Read card component docs →

See the card example and custom card example built with Dopt →

Thumbnail image for changelog item

Start flows via SDKs and APIs

You can now programmatically start flows via SDKs and APIs. We’ve exposed our flow start intent in our SDKs and APIs and you can now pass an optional force=true parameter which will forcefully start a flow for a given user despite any targeting conditions. This enables you to build experiences like starting a product tour when a user clicks a button or starting a flow by listening to events in your product (including on the backend). We’ve also updated our reset intent to accept the same parameter, allowing you to forcefully enter a user when they are reset as well. Read docs →

As a part of this work improved the usability of our targeting settings. We made the targeting options more explicit, so you now select if you’d like to target all users, target with an expression, or start via API / SDK only. We also show the setting on the start block. Read docs →

Updated JS and Node blocks API client libraries

We released two new client libraries for our blocks API.

These client libraries are thin language-specific abstractions on top of our Blocks API, allowing you to access and update blocks and flows easily. We've shifted to a different technology for generating the clients and updated the spec in the process, resulting in a few nice updates.

  • Method names are more semantic and align with methods exposed in our @dopt/react and @dopt/javascript SDKs.
  • Better error types and error handling
  • More readable generated source!

The previously released Blocks API client library @dopt/blocks-javascript-client is now deprecated.

Blocks API docs →

React component updates

We’ve migrated our React components to use vanilla-extract, a zero-runtime styling library. This resulted in smaller bundle sizes as well as improved performance as styles are no longer compiled at runtime.

This is a breaking change, so please have a look at our migration steps for more info. In short, the createTheme API has changed a bit and now requires strict adherence to the theme interface and we’ve changed the way styles are bundled so your bundler will need to be aware of how to handle CSS imports. The CSS variable interface remains the same.

We’ve also created separate exports for our component hooks for cases where you’d like to use our components in a headless fashion. These exports can be accessed from any component via @dopt/react-*/hooks. In the case of the modal component, you would import useModal from @dopt/react-modal/hooks. Using the hooks-only export will not include any of the component or theme CSS.

Duplicate flow and block

We’ve added the ability to duplicate flows and blocks! These features make it easy to reuse existing flows and blocks for new use cases. You could also use a flow as a lightweight template.

Other improvements

  • Added the ability to copy flow ID from the flow listing page
  • Improved the usability of drag and drop of fields in the flow view
  • Made block names more readable on the flow canvas