Invite-Only Early Access — Think Throo GitHub App is currently invite-only. Request access here.
2026April

State management in Twenty codebase - Part 1.2

Inspired by BulletProof React, I applied its codebase architecture concepts to the Twenty codebase.

This article focuses only on the state management in Twenty codebase.

Why learn this pattern?

Let’s be honest, your useState code is probably a mess. Mine was too.

Look, we’ve all written this:

const [websites, setWebsites] = useState([]);
const addWebsite = async (data) => {
  const newWebsite = await api.post('/websites', data);
  setWebsites([...websites, newWebsite]); 
};

We all start the same way, useState for everything. Throw in Context API when passing props gets annoying. Works fine until your app grows.

Then you’re debugging why:

  • Your app is slow because state is everywhere

  • The UI shows old data after mutations

  • Clicking fast causes weird race conditions

  • You have to manually refresh data in 10 places

Production apps like Twenty don’t have these problems because they use different patterns.

Let me show you what I learned from studying their code.

Prerequisite

  1. State management in Twenty codebase — Part 1.0

  2. State management in Twenty codebase — Part 1.1

Approach

The approach we take is simple:

  1. Pick a route, for example, {id}.twenty.com/settings/billing

  2. Locate this route in Twenty codebase.

  3. Review how the state is managed.

  4. We repeat this process for 3+ pages to establish a common pattern, see if there’s any exceptions.

In this part 1.2, you will learn about the state management in the settings/billing route for a workspace. We will find out what libraries Twenty used, how the files are structured, how the data flows to manage its state.

I reviewed the billing route and I found that the following components give us a clear picture about the state management.

  1. SettingsBilling.tsx

  2. SettingsBillingContent.tsx

SettingsBilling.tsx

In this SettingsBilling.tsx component, currentWorkspace and isPlanLoaded are defined as shown below:

const currentWorkspace = useAtomStateValue(currentWorkspaceState);
 
const { isPlansLoaded } = usePlans();

Twenty uses Jotai to manage its state. Learn more about Jotai. We also reviewed this useAtomStateValue briefly in part 1.1.

If there is current workspace and isPlansLoaded, then SettingsBillingContent is shown

{currentWorkspace && isPlansLoaded ? <SettingsBillingContent /> : <></>}

usePlans.ts

import { useQuery } from '@apollo/client/react';
import { ListPlansDocument } from '~/generated-metadata/graphql';
import { isDefined } from 'twenty-shared/utils';
 
export const usePlans = () => {
  const { data, loading, error } = useQuery(ListPlansDocument);
 
  const isPlansLoaded = isDefined(data?.listPlans);
 
  const listPlans = () => {
    if (!data) throw new Error('plans is undefined');
    return data.listPlans;
  };
 
  return { loading, error, isPlansLoaded, listPlans };
};

usePlans.ts is a hook that fetches the plans and it returns the isPlansLoaded.

SettingsBillingContent.tsx

In the SettingsBillingContent.tsx, the following are defined:

  1. SettingsBillingSubscriptionInfo

  2. SettingsBillingCreditsSection

and there’s also other components defined using primitive components H2, Button so I did not list them here and we consider only above two for review.

SettingsBillingSubscriptionInfo

This above shown screenshot is from the Twenty billing, this component SettingsBillingSubscriptionInfo renders this info.

It has the following props:

<SettingsBillingSubscriptionInfo
  currentWorkspace={currentWorkspace}
  currentBillingSubscription={
    currentWorkspace.currentBillingSubscription
  }
/>

And this currentWorkspace is defined as follows:

const currentWorkspace = useAtomStateValue(currentWorkspaceState);

SettingsBillingCreditsSection

SettingsBillingCreditsSection is defined as shown below:

{hasNotCanceledCurrentSubscription &&
  currentWorkspace &&
  currentWorkspace.currentBillingSubscription &&
  isGetMeteredProductsUsageQueryLoaded && (
  <SettingsBillingCreditsSection
    currentBillingSubscription={
      currentWorkspace.currentBillingSubscription
    }
  />
)}

isGetMeteredProductsUsageQueryLoaded is defined as shown below:

const { isGetMeteredProductsUsageQueryLoaded } =
    useGetWorkflowNodeExecutionUsage();

About me:

Hey, my name is Ramu Narasinga. Email: ramu.narasinga@gmail.com

Tired of AI slop?

I spent 3+ years studying OSS codebases and wrote 350+ articles on what makes them production-grade. I built an open source tool that reviews your PR against your existing codebase patterns.

Your codebase. Your patterns. Enforced.

Get started for free —thinkthroo.com

References

  1. twentyhq/twenty

  2. alan2207/bulletproof-react/docs/state-management.md

  3. twenty/packages/twenty-front/src/pages

  4. twenty/packages/twenty-front/src/…/SettingsBilling.tsx

  5. twenty/packages/twenty-front/src/…/SettingsBillingContent.tsx#L21

  6. https://jotai.org/

  7. twenty/blob/main/packages/twenty-front/src/modules/settings/billing/hooks/usePlans.ts#L5