diff --git a/apps/designer/app/entry.client.tsx b/apps/designer/app/entry.client.tsx
index 0a870b7dab..af4f800575 100644
--- a/apps/designer/app/entry.client.tsx
+++ b/apps/designer/app/entry.client.tsx
@@ -5,6 +5,7 @@ import { initSentry } from "./shared/sentry";
import { useLocation, useMatches } from "@remix-run/react";
import { BrowserTracing, remixRouterInstrumentation } from "@sentry/remix";
import { useEffect } from "react";
+import env from "./shared/env";
initSentry({
integrations: [
@@ -18,4 +19,14 @@ initSentry({
],
});
+// Forward DEBUG env variable defined on the server to the client-side debug() utility.
+// This way you can set it once server-side and have the logging or feature flags enabled on both, server and client
+if (
+ typeof localStorage !== "undefined" &&
+ // Client-side value should override the server-side one
+ (localStorage.debug == null || localStorage.debug === "")
+) {
+ localStorage.debug = env.DEBUG;
+}
+
hydrate(, document);
diff --git a/apps/designer/app/env.server.ts b/apps/designer/app/env.server.ts
index 90fa727197..7040eecefb 100644
--- a/apps/designer/app/env.server.ts
+++ b/apps/designer/app/env.server.ts
@@ -3,6 +3,8 @@
const env = {
SENTRY_DSN: process.env.SENTRY_DSN,
VERCEL_ENV: process.env.VERCEL_ENV,
+ DEBUG: process.env.DEBUG,
+ FEATURE: process.env.FEATURE,
} as const;
export default env;
diff --git a/apps/designer/app/shared/feature-flags/feature.ts b/apps/designer/app/shared/feature-flags/feature.ts
new file mode 100644
index 0000000000..c000c73826
--- /dev/null
+++ b/apps/designer/app/shared/feature-flags/feature.ts
@@ -0,0 +1,32 @@
+import * as flags from "./flags";
+import env from "~/shared/env";
+
+type Name = keyof typeof flags;
+
+const parse = (flags?: string | null): Array =>
+ // Supports both, space and comma separated items
+ (flags ?? "").split(/\s|,/).filter(Boolean) as Array;
+
+const readLocal = (): Array => {
+ try {
+ const flags = localStorage.getItem("feature");
+ return parse(flags);
+ } catch (_error) {
+ // Not having feature in localStorage or not having localStorage implemented, both should not throw.
+ }
+ return [];
+};
+
+/**
+ * Returns true/false if the feature is turned on.
+ * A feature can be turned on:
+ * - by default directly in ./flags
+ * - by providing an environment variable server-side (locally or on the server): FEATURE="something1, something2" yarn dev
+ * - by setting it in the browser console: localStorage.feature = 'something1, something2', browser defined flag will override server-side flag
+ */
+export const isFeatureEnabled = (name: Name): boolean => {
+ const defaultValue = flags[name];
+ const envValue = parse(env.FEATURE).includes(name);
+ const localValue = readLocal().includes(name);
+ return localValue ?? envValue ?? defaultValue;
+};
diff --git a/apps/designer/app/shared/feature-flags/flags.ts b/apps/designer/app/shared/feature-flags/flags.ts
new file mode 100644
index 0000000000..fe68be041d
--- /dev/null
+++ b/apps/designer/app/shared/feature-flags/flags.ts
@@ -0,0 +1 @@
+export const theme = false;
diff --git a/apps/designer/app/shared/feature-flags/index.ts b/apps/designer/app/shared/feature-flags/index.ts
new file mode 100644
index 0000000000..d0d5122433
--- /dev/null
+++ b/apps/designer/app/shared/feature-flags/index.ts
@@ -0,0 +1 @@
+export * from "./feature";
diff --git a/apps/designer/package.json b/apps/designer/package.json
index bbda01c018..8614ff88a7 100644
--- a/apps/designer/package.json
+++ b/apps/designer/package.json
@@ -59,6 +59,7 @@
"@webstudio-is/sdk": "0.4.1",
"bson-objectid": "^2.0.2",
"camelcase": "^6.3.0",
+ "debug": "^4.3.4",
"hyphenate-style-name": "^1.0.4",
"immer": "^9.0.12",
"immerhin": "^0.1.2",