diff --git a/content/docs/tutorial/dynamic-enum.mdx b/content/docs/tutorial/dynamic-enum.mdx
new file mode 100644
index 00000000..2761b9e5
--- /dev/null
+++ b/content/docs/tutorial/dynamic-enum.mdx
@@ -0,0 +1,260 @@
+---
+id: dynamic-enum
+title: Dynamic Renderers
+description: This tutorial describes how to create a dynamic enum
+---
+
+import { WithRegionRenderer } from '../../../src/components/docs/tutorials/dynamic-enum';
+
+
+In this tutorial, you will learned how to handle dynamic data in React using [custom renderers](./custom-renderers), React Context, and the `useJsonForms` hook.
+This approach allows you to build flexible and interactive forms that adapt to user selections and API responses.
+
+### Scenario
+
+Imagine a form where users need to provide their location by selecting a country, a region and a city.
+The options for countries and regions are fetched from an API.
+The available regions depend on the selected country.
+To tacks those requirements. we'll create custom renderers for the country and region.
+The example is also implemented in the [react-seed](https://github.com/eclipsesource/jsonforms-react-seed) app.
+
+
+
+
+#### Schema
+
+To begin, let's introduce the corresponding JSON schema.
+We have created an object with properties for country, region, and city.
+In our example, the schema also includes a property `x-url`, which specifies the entry point of the corresponding API.
+Both `country` and `region` have a property `endpoint`, indicating the endpoint from which the data should be fetched.
+Additionally, they have a field specifying which fields depend on the input.
+In the case of the `country` field, the `region` and `city` fields depend on it and will get reset, if the value of the `country` changes.
+The `city` field, in turn, is dependent on the `region` field.
+
+```js
+{
+ "type": "object",
+ "x-url": "www.api.com",
+ "properties": {
+ "country": {
+ "type": "string",
+ "x-endpoint": "countries",
+ "dependencies": ["region", "city"]
+ },
+ "region": {
+ "type": "string",
+ "x-endpoint": "regions",
+ "dependencies": ["city"]
+ },
+ "city": {
+ "type": "string"
+ }
+ }
+}
+```
+
+
+### Accessing Schema Data and Initialising the React Context
+
+In this step we will access the data from the schema and initialize the react context.
+
+#### Accessing the API URL from Schema
+
+To access the URL defined from the schema we can simply access the `x-url` attribute.
+
+```js
+const url = schema['x-url'];
+```
+
+#### Initializing the React Context
+
+Now that we have access to the API URL, we can use React Context to make this data available across our renderers.
+React Context allows you to share data globally within your application, enabling components deep in the component tree to access data without needing to pass properties through all parent elements.
+To set up the React Context for your API service, create it in your application as follows:
+
+```js
+export const APIContext = React.createContext(new API(url));
+
+const App = () =>{
+
+ ...
+
+}
+```
+
+### The Country Renderer
+
+The core of the country renderer is a dropdown, we can reuse the MaterialEnumControl from the material-renderer set.
+To reuse material renderers, the Unwrapped renderers must be used. (more information regarding reusing renderers can be seen [here](./custom-renderers#reusing-existing-controls))
+
+```js
+import { Unwrapped, WithOptionLabel } from '@jsonforms/material-renderers';
+
+const { MaterialEnumControl } = Unwrapped;
+
+...
+
+
+...
+```
+
+With the `MaterialEnumControl`in place the main question remains how to set the `options` and the `handleChange` attribute.
+To determine the available options, we need to access the API.
+And to implement the `handleChange` function, we need access to the `dependened` field in the schema.
+
+#### Accessing the API context
+
+
+Access the API service using the context:
+
+```js
+const api = React.useContext(APIContext);
+```
+
+Changing the context's value will trigger a re-render of components that use it, making it a powerful tool for managing dynamic data.
+
+#### Accessing Schema Data
+
+The `endpoint` and `dependent` fields can be obtained from the schema object provided to the custom renderer via JSON Forms.
+Since these fields are not part of the standard JSON schema type in JSON Forms, we must add them to the schema´s interface and access them as follows:
+
+```js
+type JsonSchemaWithDependenciesAndEndpoint = JsonSchema & {
+ dependened: string[];
+ endpoint: string;
+};
+export const Country = (
+ props: ControlProps & OwnPropsOfEnum & WithOptionLabel & TranslateProps
+) => {
+...
+
+ const schema = props.schema as JsonSchemaWithDependenciesAndEndpoint;
+ const endponit = schema.endpoint;
+ const dependened = schema.dependened
+...
+}
+```
+
+#### The Country Renderer
+
+The country renderer uses the `APIContext` to query the API and fetch the available options.
+We utilize the `useEffect` hook to reload new options, if API changes.
+While waiting for the API response, we set the available options to empty and display a loading spinner.
+In the `handleChange` function, we set the new selected value and reset all dependent fields;
+When changing the country, both the region and city will be reset to `undefined`.
+
+```js
+import { Unwrapped, WithOptionLabel } from '@jsonforms/material-renderers';
+
+const { MaterialEnumControl } = Unwrapped;
+
+type JsonSchemaWithDependenciesAndEndpoint = JsonSchema & {
+ dependened: string[];
+ endpoint: string;
+};
+
+export const Country = (
+ props: ControlProps & OwnPropsOfEnum & WithOptionLabel & TranslateProps
+) => {
+ const { handleChange } = props;
+ const [options, setOptions] = useState([]);
+ const api = React.useContext(APIContext);
+ const schema = props.schema as JsonSchemaWithDependenciesAndEndpoint;
+
+ const endponit = schema.endpoint;
+ const dependened: string[] = schema.dependened ? schema.dependened : [];
+
+ useEffect(() => {
+ setOptions([]);
+ api.get(endponit).then((result) => {
+ setOptions(result);
+ });
+ }, [api, endponit]);
+
+ if (options.length === 0) {
+ return ;
+ }
+
+ return (
+ {
+ handleChange(path, value);
+ dependened.forEach((path) => {
+ handleChange(path, undefined);
+ });
+ }}
+ options={options.map((option) => {
+ return { label: option, value: option };
+ })}
+ />
+ );
+};
+```
+
+Now all that´s left to do is to [create a tester](./custom-renderers#2-create-a-tester) and [register](./custom-renderers#3-register-the-renderer) the new custom renderer in our application.
+
+### The Region Renderer
+
+The region renderer can be implemented similarly to the country renderer.
+It also accesses the API via the context and includes `endpoint` and `dependent` fields defined in its schema.
+However, the options, on the other hand, are also dependent on the selected country.
+JSON Forms provides the `useJsonForms` hook, allowing you to access form data and trigger component rerenders when the data changes.
+Let's use this hook in our region renderer to access the selected country:
+
+```js
+import { Unwrapped, WithOptionLabel } from '@jsonforms/material-renderers';
+const { MaterialEnumControl } = Unwrapped;
+
+type JsonSchemaWithDependenciesAndEndpont = JsonSchema & {
+ dependened: string[];
+ endpoint: string;
+};
+
+export const Region = (
+ props: ControlProps & OwnPropsOfEnum & WithOptionLabel & TranslateProps
+) => {
+ const schema = props.schema as JsonSchemaWithDependenciesAndEndpont;
+ const { handleChange } = props;
+ const [options, setOptions] = useState([]);
+ const api = React.useContext(APIContext);
+ const country = useJsonForms().core?.data.country;
+ const [previousCountry, setPreviousCountry] = useState();
+
+ const endponit = schema.endpoint;
+ const dependened: string[] = schema.dependened ? schema.dependened : [];
+
+ if (previousCountry !== country) {
+ setOptions([]);
+ setPreviousCountry(country);
+ api.get(endponit + '/' + country).then((result) => {
+ setOptions(result);
+ });
+ }
+
+ if (options.length === 0 && country !== undefined) {
+ return ;
+ }
+
+ return (
+ {
+ handleChange(path, value);
+ dependened.forEach((path) => {
+ handleChange(path, undefined);
+ });
+ }}
+ options={options.map((option) => {
+ return { label: option, value: option };
+ })}
+ />
+ );
+};
+```
+Again we need to create a [create a tester](./custom-renderers#2-create-a-tester) and [register](./custom-renderers#3-register-the-renderer) the new custom renderer.
+A running example of the scenario can also be seen at the [react-seed](https://github.com/eclipsesource/jsonforms-react-seed) app.
\ No newline at end of file
diff --git a/docusaurus.config.js b/docusaurus.config.js
index 710344ff..784104f5 100644
--- a/docusaurus.config.js
+++ b/docusaurus.config.js
@@ -225,6 +225,10 @@ module.exports = {
to: '/docs/tutorial/custom-renderers',
from: '/docs/custom-renderers',
},
+ {
+ to: '/docs/tutorial/dynamic-enum',
+ from: '/docs/dynamic-enum',
+ },
{
to: '/docs/tutorial/multiple-forms',
from: '/docs/multiple-forms',
diff --git a/src/components/common/api.ts b/src/components/common/api.ts
new file mode 100644
index 00000000..a32746ef
--- /dev/null
+++ b/src/components/common/api.ts
@@ -0,0 +1,92 @@
+export class API {
+ private url: string;
+
+ constructor(url: string) {
+ this.url = url;
+ }
+
+ async get(endpoint: string): Promise {
+ switch (this.url + '/' + endpoint) {
+ case 'www.api.com/regions/Germany':
+ return germanStates;
+ case 'www.api.com/regions/US':
+ return usStates;
+ case 'www.api.com/countries':
+ return ['Germany', 'US'];
+ default:
+ return [];
+ }
+ }
+}
+
+const germanStates = [
+ 'Berlin',
+ 'Bayern',
+ 'Niedersachsen',
+ 'Baden-Württemberg',
+ 'Rheinland-Pfalz',
+ 'Sachsen',
+ 'Thüringen',
+ 'Hessen',
+ 'Nordrhein-Westfalen',
+ 'Sachsen-Anhalt',
+ 'Brandenburg',
+ 'Mecklenburg-Vorpommern',
+ 'Hamburg',
+ 'Schleswig-Holstein',
+ 'Saarland',
+ 'Bremen',
+];
+
+const usStates = [
+ 'Alabama',
+ 'Alaska',
+ 'Arizona',
+ 'Arkansas',
+ 'California',
+ 'Colorado',
+ 'Connecticut',
+ 'Delaware',
+ 'Florida',
+ 'Georgia',
+ 'Hawaii',
+ 'Idaho',
+ 'Illinois',
+ 'Indiana',
+ 'Iowa',
+ 'Kansas',
+ 'Kentucky',
+ 'Louisiana',
+ 'Maine',
+ 'Maryland',
+ 'Massachusetts',
+ 'Michigan',
+ 'Minnesota',
+ 'Mississippi',
+ 'Missouri',
+ 'Montana',
+ 'Nebraska',
+ 'Nevada',
+ 'New Hampshire',
+ 'New Jersey',
+ 'New Mexico',
+ 'New York',
+ 'North Carolina',
+ 'North Dakota',
+ 'Ohio',
+ 'Oklahoma',
+ 'Oregon',
+ 'Pennsylvania',
+ 'Rhode Island',
+ 'South Carolina',
+ 'South Dakota',
+ 'Tennessee',
+ 'Texas',
+ 'Utah',
+ 'Vermont',
+ 'Virginia',
+ 'Washington',
+ 'West Virginia',
+ 'Wisconsin',
+ 'Wyoming',
+];
diff --git a/src/components/common/country/Country.tsx b/src/components/common/country/Country.tsx
new file mode 100644
index 00000000..c37e591e
--- /dev/null
+++ b/src/components/common/country/Country.tsx
@@ -0,0 +1,52 @@
+import React, { useEffect } from 'react';
+import { useState } from 'react';
+import { ControlProps, JsonSchema, OwnPropsOfEnum } from '@jsonforms/core';
+import { TranslateProps } from '@jsonforms/react';
+import { CircularProgress } from '@mui/material';
+import { Unwrapped, WithOptionLabel } from '@jsonforms/material-renderers';
+import { APIContext } from '../../docs/tutorials/dynamic-enum';
+
+const { MaterialEnumControl } = Unwrapped;
+
+type JsonSchemaWithDependenciesAndEndpoint = JsonSchema & {
+ dependened: string[];
+ endpoint: string;
+};
+
+export const Country = (
+ props: ControlProps & OwnPropsOfEnum & WithOptionLabel & TranslateProps
+) => {
+ const { handleChange } = props;
+ const [options, setOptions] = useState([]);
+ const api = React.useContext(APIContext);
+ const schema = props.schema as JsonSchemaWithDependenciesAndEndpoint;
+
+ const endponit = schema.endpoint;
+ const dependened: string[] = schema.dependened ? schema.dependened : [];
+
+ useEffect(() => {
+ setOptions([]);
+ api.get(endponit).then((result) => {
+ setOptions(result);
+ });
+ }, [api, endponit]);
+
+ if (options.length === 0) {
+ return ;
+ }
+
+ return (
+ {
+ handleChange(path, value);
+ dependened.forEach((path) => {
+ handleChange(path, undefined);
+ });
+ }}
+ options={options.map((option) => {
+ return { label: option, value: option };
+ })}
+ />
+ );
+};
\ No newline at end of file
diff --git a/src/components/common/country/CountryControl.tsx b/src/components/common/country/CountryControl.tsx
new file mode 100644
index 00000000..306005ba
--- /dev/null
+++ b/src/components/common/country/CountryControl.tsx
@@ -0,0 +1,18 @@
+import {
+ TranslateProps,
+ withTranslateProps,
+ withJsonFormsEnumProps,
+} from '@jsonforms/react';
+import { Country } from './Country';
+import { ControlProps, OwnPropsOfEnum } from '@jsonforms/core';
+import { WithOptionLabel } from '@jsonforms/material-renderers';
+import React from 'react';
+
+export const CountryControl = (
+ props: ControlProps & OwnPropsOfEnum & WithOptionLabel & TranslateProps
+) => ;
+
+export default withJsonFormsEnumProps(
+ withTranslateProps(React.memo(CountryControl)),
+ false
+);
diff --git a/src/components/common/country/countryControlTester.ts b/src/components/common/country/countryControlTester.ts
new file mode 100644
index 00000000..a6a7d559
--- /dev/null
+++ b/src/components/common/country/countryControlTester.ts
@@ -0,0 +1,6 @@
+import { rankWith, scopeEndsWith } from '@jsonforms/core';
+
+export default rankWith(
+ 3, //increase rank as needed
+ scopeEndsWith('country')
+);
diff --git a/src/components/common/region/Region.tsx b/src/components/common/region/Region.tsx
new file mode 100644
index 00000000..6810587c
--- /dev/null
+++ b/src/components/common/region/Region.tsx
@@ -0,0 +1,54 @@
+import React from 'react';
+import { useState } from 'react';
+import { ControlProps, JsonSchema, OwnPropsOfEnum } from '@jsonforms/core';
+import { TranslateProps, useJsonForms } from '@jsonforms/react';
+import { CircularProgress } from '@mui/material';
+import { Unwrapped, WithOptionLabel } from '@jsonforms/material-renderers';
+import { APIContext } from '../../docs/tutorials/dynamic-enum';
+const { MaterialEnumControl } = Unwrapped;
+
+type JsonSchemaWithDependenciesAndEndpont = JsonSchema & {
+ dependened: string[];
+ endpoint: string;
+};
+
+export const Region = (
+ props: ControlProps & OwnPropsOfEnum & WithOptionLabel & TranslateProps
+) => {
+ const schema = props.schema as JsonSchemaWithDependenciesAndEndpont;
+ const { handleChange } = props;
+ const [options, setOptions] = useState([]);
+ const api = React.useContext(APIContext);
+ const country = useJsonForms().core?.data.country;
+ const [previousCountry, setPreviousCountry] = useState();
+
+ const endponit = schema.endpoint;
+ const dependened: string[] = schema.dependened ? schema.dependened : [];
+
+ if (previousCountry !== country) {
+ setOptions([]);
+ setPreviousCountry(country);
+ api.get(endponit + '/' + country).then((result) => {
+ setOptions(result);
+ });
+ }
+
+ if (options.length === 0 && country !== undefined) {
+ return ;
+ }
+
+ return (
+ {
+ handleChange(path, value);
+ dependened.forEach((path) => {
+ handleChange(path, undefined);
+ });
+ }}
+ options={options.map((option) => {
+ return { label: option, value: option };
+ })}
+ />
+ );
+};
diff --git a/src/components/common/region/RegionControl.tsx b/src/components/common/region/RegionControl.tsx
new file mode 100644
index 00000000..0b1665ec
--- /dev/null
+++ b/src/components/common/region/RegionControl.tsx
@@ -0,0 +1,18 @@
+import {
+ TranslateProps,
+ withJsonFormsEnumProps,
+ withTranslateProps,
+} from '@jsonforms/react';
+import { Region } from './Region';
+import { ControlProps, OwnPropsOfEnum } from '@jsonforms/core';
+import { WithOptionLabel } from '@jsonforms/material-renderers';
+import React from 'react';
+
+export const RegionControl = (
+ props: ControlProps & OwnPropsOfEnum & WithOptionLabel & TranslateProps
+) => ;
+
+export default withJsonFormsEnumProps(
+ withTranslateProps(React.memo(RegionControl)),
+ false
+);
diff --git a/src/components/common/region/regionControlTester.ts b/src/components/common/region/regionControlTester.ts
new file mode 100644
index 00000000..d81ed49c
--- /dev/null
+++ b/src/components/common/region/regionControlTester.ts
@@ -0,0 +1,6 @@
+import { rankWith, scopeEndsWith } from '@jsonforms/core';
+
+export default rankWith(
+ 3, //increase rank as needed
+ scopeEndsWith('region')
+);
diff --git a/src/components/docs/tutorials/dynamic-enum.js b/src/components/docs/tutorials/dynamic-enum.js
new file mode 100644
index 00000000..a3047759
--- /dev/null
+++ b/src/components/docs/tutorials/dynamic-enum.js
@@ -0,0 +1,66 @@
+import { Demo } from '../../common/Demo';
+import { materialRenderers } from '@jsonforms/material-renderers';
+import React from 'react';
+
+import countryControlTester from '../../common/country/countryControlTester';
+import CountryControl from '../../common/country/CountryControl';
+import regionControlTester from '../../common/region/regionControlTester';
+import RegionControl from '../../common/region/RegionControl';
+import { API } from '../../common/api';
+
+const data = {
+};
+
+const schema = {
+ "x-url": "www.api.com",
+ "type": "object",
+ "properties": {
+ "country": {
+ "type": "string",
+ "endpoint": "countries",
+ "dependened": ["region", "city"]
+ },
+ "region": {
+ "type": "string",
+ "endpoint": "regions",
+ "dependened": ["city"]
+ },
+ "city": {
+ "type": "string"
+ },
+ }}
+
+
+const regionUiSchema = {
+ "type": "HorizontalLayout",
+ "elements": [
+ {
+ "type": "Control",
+ "scope": "#/properties/country"
+ },
+ {
+ "type": "Control",
+ "scope": "#/properties/region"
+ },
+ {
+ "type": "Control",
+ "scope": "#/properties/city"
+ }
+ ]
+ }
+
+export const WithRegionRenderer = () => (
+
+);
+
+const url = schema['x-url'];
+export const APIContext = React.createContext(new API(url));
\ No newline at end of file
diff --git a/src/sidebars/docs.js b/src/sidebars/docs.js
index b31cbead..cdde757a 100644
--- a/src/sidebars/docs.js
+++ b/src/sidebars/docs.js
@@ -21,7 +21,7 @@ module.exports = {
type: 'category',
label: 'Tutorials',
collapsed: false,
- items: ['tutorial/create-app', 'tutorial/custom-layouts', 'tutorial/custom-renderers', 'tutorial/multiple-forms'],
+ items: ['tutorial/create-app', 'tutorial/custom-layouts', 'tutorial/custom-renderers', 'tutorial/dynamic-enum', 'tutorial/multiple-forms'],
},
'api',
{