diff --git a/examples/feature_flags_example/public/components/app.tsx b/examples/feature_flags_example/public/components/app.tsx
index 432e7dc348abc..97f850b5dd475 100644
--- a/examples/feature_flags_example/public/components/app.tsx
+++ b/examples/feature_flags_example/public/components/app.tsx
@@ -8,24 +8,15 @@
*/
import React from 'react';
-import {
- EuiHorizontalRule,
- EuiPageTemplate,
- EuiTitle,
- EuiText,
- EuiLink,
- EuiListGroup,
- EuiListGroupItem,
-} from '@elastic/eui';
+import { EuiHorizontalRule, EuiPageTemplate, EuiTitle, EuiText, EuiLink } from '@elastic/eui';
import type { CoreStart, FeatureFlagsStart } from '@kbn/core/public';
-import useObservable from 'react-use/lib/useObservable';
-import {
- FeatureFlagExampleBoolean,
- FeatureFlagExampleNumber,
- FeatureFlagExampleString,
-} from '../../common/feature_flags';
import { PLUGIN_NAME } from '../../common';
+import {
+ FeatureFlagsFullList,
+ FeatureFlagsReactiveList,
+ FeatureFlagsStaticList,
+} from './feature_flags_list';
interface FeatureFlagsExampleAppDeps {
featureFlags: FeatureFlagsStart;
@@ -34,16 +25,6 @@ interface FeatureFlagsExampleAppDeps {
}
export const FeatureFlagsExampleApp = ({ featureFlags }: FeatureFlagsExampleAppDeps) => {
- // Fetching the feature flags synchronously
- const bool = featureFlags.getBooleanValue(FeatureFlagExampleBoolean, false);
- const str = featureFlags.getStringValue(FeatureFlagExampleString, 'red');
- const num = featureFlags.getNumberValue(FeatureFlagExampleNumber, 1);
-
- // Use React Hooks to observe feature flags changes
- const bool$ = useObservable(featureFlags.getBooleanValue$(FeatureFlagExampleBoolean, false));
- const str$ = useObservable(featureFlags.getStringValue$(FeatureFlagExampleString, 'red'));
- const num$ = useObservable(featureFlags.getNumberValue$(FeatureFlagExampleNumber, 1));
-
return (
<>
@@ -67,22 +48,21 @@ export const FeatureFlagsExampleApp = ({ featureFlags }: FeatureFlagsExampleAppD
.
-
-
- The feature flags are:
-
-
-
-
-
-
-
- The observed feature flags are:
-
-
-
-
-
+ Rendered separately
+
+ Each list are 2 different components, so only the reactive one is re-rendered when the
+ feature flag is updated and the static one keeps the value until the next refresh.
+
+
+
+
+ Rendered together
+
+ `useObservable` causes a full re-render of the component, updating the{' '}
+ statically
+ evaluated flags as well.
+
+
diff --git a/examples/feature_flags_example/public/components/feature_flags_list.tsx b/examples/feature_flags_example/public/components/feature_flags_list.tsx
new file mode 100644
index 0000000000000..73d6535295865
--- /dev/null
+++ b/examples/feature_flags_example/public/components/feature_flags_list.tsx
@@ -0,0 +1,91 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the "Elastic License
+ * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+import { EuiListGroup, EuiListGroupItem } from '@elastic/eui';
+import React from 'react';
+import type { FeatureFlagsStart } from '@kbn/core-feature-flags-browser';
+import useObservable from 'react-use/lib/useObservable';
+import {
+ FeatureFlagExampleBoolean,
+ FeatureFlagExampleNumber,
+ FeatureFlagExampleString,
+} from '../../common/feature_flags';
+
+export interface FeatureFlagsListProps {
+ featureFlags: FeatureFlagsStart;
+}
+
+export const FeatureFlagsStaticList = ({ featureFlags }: FeatureFlagsListProps) => {
+ // Fetching the feature flags synchronously
+ const bool = featureFlags.getBooleanValue(FeatureFlagExampleBoolean, false);
+ const str = featureFlags.getStringValue(FeatureFlagExampleString, 'red');
+ const num = featureFlags.getNumberValue(FeatureFlagExampleNumber, 1);
+
+ return (
+
+
+ The feature flags are:
+
+
+
+
+
+ );
+};
+
+export const FeatureFlagsReactiveList = ({ featureFlags }: FeatureFlagsListProps) => {
+ // Use React Hooks to observe feature flags changes
+ const bool$ = useObservable(featureFlags.getBooleanValue$(FeatureFlagExampleBoolean, false));
+ const str$ = useObservable(featureFlags.getStringValue$(FeatureFlagExampleString, 'red'));
+ const num$ = useObservable(featureFlags.getNumberValue$(FeatureFlagExampleNumber, 1));
+
+ return (
+
+
+ The observed feature flags are:
+
+
+
+
+
+ );
+};
+
+export const FeatureFlagsFullList = ({ featureFlags }: FeatureFlagsListProps) => {
+ // Fetching the feature flags synchronously
+ const bool = featureFlags.getBooleanValue(FeatureFlagExampleBoolean, false);
+ const str = featureFlags.getStringValue(FeatureFlagExampleString, 'red');
+ const num = featureFlags.getNumberValue(FeatureFlagExampleNumber, 1);
+
+ // Use React Hooks to observe feature flags changes
+ const bool$ = useObservable(featureFlags.getBooleanValue$(FeatureFlagExampleBoolean, false));
+ const str$ = useObservable(featureFlags.getStringValue$(FeatureFlagExampleString, 'red'));
+ const num$ = useObservable(featureFlags.getNumberValue$(FeatureFlagExampleNumber, 1));
+
+ return (
+ <>
+
+
+ The feature flags are:
+
+
+
+
+
+
+
+ The observed feature flags are:
+
+
+
+
+
+ >
+ );
+};
diff --git a/examples/feature_flags_example/tsconfig.json b/examples/feature_flags_example/tsconfig.json
index bbd68332f3d37..77eb5d09ca85b 100644
--- a/examples/feature_flags_example/tsconfig.json
+++ b/examples/feature_flags_example/tsconfig.json
@@ -20,5 +20,6 @@
"@kbn/core-plugins-server",
"@kbn/config-schema",
"@kbn/developer-examples-plugin",
+ "@kbn/core-feature-flags-browser",
]
}
diff --git a/packages/core/feature-flags/README.mdx b/packages/core/feature-flags/README.mdx
index 99e10b72aa68c..8a28fdf35932b 100644
--- a/packages/core/feature-flags/README.mdx
+++ b/packages/core/feature-flags/README.mdx
@@ -15,7 +15,7 @@ The service is always enabled, however, it will return the fallback value if a f
Kibana only registers a provider when running on Elastic Cloud Hosted/Serverless. And even in those scenarios, we expect that some customers might
have network restrictions that might not allow the flags to evaluate. The fallback value must provide a non-broken experience to users.
-:warning: Feature Flags are considered dynamic configuration and cannot be used for settings that require restarting Kibana.
+⚠️Feature Flags are considered dynamic configuration and cannot be used for settings that require restarting Kibana.
One example of invalid use cases are settings used during the `setup` lifecycle of the plugin, such as settings that define
if an HTTP route is registered or not. Instead, you should always register the route, and return `404 - Not found` in the route
handler if the feature flag returns a _disabled_ state.
diff --git a/packages/core/feature-flags/core-feature-flags-browser-internal/src/feature_flags_service.ts b/packages/core/feature-flags/core-feature-flags-browser-internal/src/feature_flags_service.ts
index e282cc52ddd4e..afc32d93aee3c 100644
--- a/packages/core/feature-flags/core-feature-flags-browser-internal/src/feature_flags_service.ts
+++ b/packages/core/feature-flags/core-feature-flags-browser-internal/src/feature_flags_service.ts
@@ -68,7 +68,15 @@ export class FeatureFlagsService {
if (this.isProviderReadyPromise) {
throw new Error('A provider has already been set. This API cannot be called twice.');
}
+ const transaction = apm.startTransaction('set-provider', 'feature-flags');
this.isProviderReadyPromise = OpenFeature.setProviderAndWait(provider);
+ this.isProviderReadyPromise
+ .then(() => transaction?.end())
+ .catch((err) => {
+ this.logger.error(err);
+ apm.captureError(err);
+ transaction?.end();
+ });
},
appendContext: (contextToAppend) => this.appendContext(contextToAppend),
};