Toggle Kit
A simple no-dependency feature flag library
Toggle Kit is a simple feature flag library made in TypeScript. The library has no external dependencies, and doesn’t require any backend to work. All plain simple TypeScript, and some if-statements.
Why I built it
I have had a lot of thoughts on building a library/package using TypeScript, and I didn’t really want the package to be super complex or big. It should be a simple little package that could help out a ton.
At the company I have worked at, we have used some different feature flag SaaS providers, and we haven’t really found any providers that met our criteria. At the company we had a lot of page views, which meant requests to their backend, which would make the bill explode sometimes.
After we have had these issue with the different feature flag SaaS providers, we thought, why not create it ourselves. That’s when I thought, why not build a library that we can utilize to build the service, and then the idea sparked. I have always thought:
Why does the feature flag need a backend, when you either way define the logic in the code? Can’t it all just be in the code?
I started to think, if it was possible to have some if statements and some math to do the randomness, and then make the code do the feature flagging, instead of having a backend that returns the flags, and their conditions.
Features & Requirements
After sketching out the idea, and thinking of the best way to approach this problem. I sketched out some requirements of the library:
- No dependencies on external services (backend, databases, etc.)
- Should be simple and be flexible
- Logic and definition of the flags should live alongside the code.
- The library should have an excellent developer experience
Architecture & Design Decisions
Since the library should be as lightweight and simple as possible, I didn’t want to have any external dependencies/packages installed. Everything should be written in plain TypeScript. I wanted it to be open-source as well, so the code complexity shouldn’t be too complex. When you boil the library’s logic down, it’s technically a long list of if statements with some name to them.
After coming up with the requirements, and flashing out the design decisions, I started the project. I choose to do a Test-Driven-Development (TDD), since I thought that it would be nice to have some unit tests, to make sure that everything worked as expected, but also to make sure that I did cover all test cases.
One of the requirements were that the developer experience should be top-notch, and in this case that meant having as much IntelliSense, and auto-completion as possible, to make it easier to implement and use across a big codebase. The downside of having this, meant that the library ended up including some complex type to make the IntelliSense and auto-completion to work.
export type Condition<PropertyNames> =
// String Attribute
| {
type: "equal" | "contains" | "startsWith" | "endsWith";
attribute: PropertyNames;
expectedValue: string;
}
| //...;
export type Property<PropertyNames extends string> = Record<
PropertyNames,
string | number | boolean
>;
export type FeatureFlag<PropertyNames = {}, FlagNames = {}> = {
name: FlagNames;
conditions: Condition<PropertyNames>[];
};
export type FeatureFlagClient<FlagNames> = {
isEnabled: (featureName: FlagNames) => boolean;
};As said, the library should be as simple as possible, so the architecture of the library should stay simple as well. The structure ended up having a central client, which you pass your flags into, and then use across the codebase.

One great indirect feature of this library, was that I built the library with openness in mind. This meant that instead of creating the flags inside of the client, you could create the flags somewhere else and then pass the flags into the client during runtime. This makes sense when we get to the results.
Installation & Developer Experience
A simple package, should also be simple to get started using. Therefore did I include a little snippet in the README.md file of the GitHub repository, which shows a simple example on how to get started.
import { createFeatureFlagClient } from "toggle-kit";
const client = createFeatureFlagClient({
property: {
userId: "eb10e5c2-e3f4-46fc-a6fd-f2ddba0973fb",
email: "[email protected]",
age: 21,
isAdmin: false,
},
flags: [
{
name: "secret-page",
conditions: [
{
type: "equal",
attribute: "email",
expectedValue: "[email protected]",
},
],
},
],
});
const allowSecretPage = client.isEnabled("secret-page");
console.log(allowSecretPage); // FalseThis simple example would help developers get started using the library, without having to create any complex configuration or setup. This simply sets up the library, and you are ready to use feature flags.
I choose to split the more complex guides into their own documentation, so they wouldn’t interfere with the README.md file. In here there are guides on how to set up a more complex configuration and use the types correct to the that sweet IntelliSense.
Challenges & Lessons
During this small side-project, I learned a lot about using more complex types in TypeScript, and especially generic types, which allows for some very nice things. These types are the backbone of this library.
Some of the more challenging stuff, was how would you structure this library and how should the documentation be written, so other people can understand it. The library should be easy to navigate in for contributors, and should be easy to setup and configure.
Results & Implementations
After I published the package, it was ready to be used. At the company, we had one more requirement, and that was to be able to control from our back office. This meant that we needed a service, which would create the flags and store it somewhere. Since the validation of the flags are done on the client side, we technically only needed to structure the flags with their conditions.
Our solution came to be, that we had an API, which we used to send CRUD operations to. Then this API would fetch a static JSON file from an AWS S3 bucket, mutate the data to the correct structure and save it. This meant that we could serve this S3 file via CloudFront as the CDN to the client directly, and we would have “infinitely”, on-the-edge and much cheaper feature flags.
The solution came out to be used a couple of different places, and because the library is so simple to set up, we managed to only add a few lines of code, and create the management service within a couple of days.
The library itself, ended up with zero dependencies and a bundle size of 3.6kb and 1.1kb when gzipped. Feel free to check out Toggle-Kit on Github, let me know if you start using it at your company or in one of your side projects.