diff --git a/README.md b/README.md
index b597906077..d79335db57 100644
--- a/README.md
+++ b/README.md
@@ -1803,7 +1803,12 @@ This component follows [JSON Schema](http://json-schema.org/documentation.html)
* `additionalItems` keyword for arrays
This keyword works when `items` is an array. `additionalItems: true` is not supported because there's no widget to represent an item of any type. In this case it will be treated as no additional items allowed. `additionalItems` being a valid schema is supported.
* `anyOf`, `allOf`, and `oneOf`, or multiple `types` (i.e. `"type": ["string", "array"]`
- Nobody yet has come up with a PR that adds this feature with a simple and easy-to-understand UX.
+ The `anyOf` keyword is supported but has the following caveats:
+ - The `anyOf` keyword is not supported when used inside the `items` keyword
+ for arrays.
+ - Properties declared inside the `anyOf` should not overlap with properties
+ "outside" of the `anyOf`.
+
You can use `oneOf` with [schema dependencies](#schema-dependencies) to dynamically add schema properties based on input data but this feature does not bring general support for `oneOf` elsewhere in a schema.
## Tips and tricks
@@ -1876,7 +1881,12 @@ $ git push --tags origin
### Q: Does rjsf support `oneOf`, `anyOf`, multiple types in an array, etc.?
-A: Not yet (except for a special case where you can use `oneOf` in [schema dependencies](#schema-dependencies)), but perhaps you will be the person whose PR will finally add the feature in a way that gets merged. For inspiration, see [#329](https://github.com/mozilla-services/react-jsonschema-form/pull/329) or [#417](https://github.com/mozilla-services/react-jsonschema-form/pull/417). See also: [#52](https://github.com/mozilla-services/react-jsonschema-form/issues/52), [#151](https://github.com/mozilla-services/react-jsonschema-form/issues/151), [#171](https://github.com/mozilla-services/react-jsonschema-form/issues/171), [#200](https://github.com/mozilla-services/react-jsonschema-form/issues/200), [#282](https://github.com/mozilla-services/react-jsonschema-form/issues/282), [#302](https://github.com/mozilla-services/react-jsonschema-form/pull/302), [#330](https://github.com/mozilla-services/react-jsonschema-form/issues/330), [#430](https://github.com/mozilla-services/react-jsonschema-form/issues/430), [#522](https://github.com/mozilla-services/react-jsonschema-form/issues/522), [#538](https://github.com/mozilla-services/react-jsonschema-form/issues/538), [#551](https://github.com/mozilla-services/react-jsonschema-form/issues/551), [#552](https://github.com/mozilla-services/react-jsonschema-form/issues/552), or [#648](https://github.com/mozilla-services/react-jsonschema-form/issues/648).
+A: The `anyOf` keyword is supported but has the following caveats:
+ - The `anyOf` keyword is not supported when used inside the `items` keyword
+ for arrays.
+ - Properties declared inside the `anyOf` should not overlap with properties
+ "outside" of the `anyOf`.
+There is also special cased where you can use `oneOf` in [schema dependencies](#schema-dependencies), If you'd like to help improve support for these keywords, see the following issues for inspiration [#329](https://github.com/mozilla-services/react-jsonschema-form/pull/329) or [#417](https://github.com/mozilla-services/react-jsonschema-form/pull/417). See also: [#52](https://github.com/mozilla-services/react-jsonschema-form/issues/52), [#151](https://github.com/mozilla-services/react-jsonschema-form/issues/151), [#171](https://github.com/mozilla-services/react-jsonschema-form/issues/171), [#200](https://github.com/mozilla-services/react-jsonschema-form/issues/200), [#282](https://github.com/mozilla-services/react-jsonschema-form/issues/282), [#302](https://github.com/mozilla-services/react-jsonschema-form/pull/302), [#330](https://github.com/mozilla-services/react-jsonschema-form/issues/330), [#430](https://github.com/mozilla-services/react-jsonschema-form/issues/430), [#522](https://github.com/mozilla-services/react-jsonschema-form/issues/522), [#538](https://github.com/mozilla-services/react-jsonschema-form/issues/538), [#551](https://github.com/mozilla-services/react-jsonschema-form/issues/551), [#552](https://github.com/mozilla-services/react-jsonschema-form/issues/552), or [#648](https://github.com/mozilla-services/react-jsonschema-form/issues/648).
### Q: Will react-jsonschema-form support Material, Ant-Design, Foundation, or [some other specific widget library or frontend style]?
diff --git a/playground/samples/anyOf.js b/playground/samples/anyOf.js
new file mode 100644
index 0000000000..432b0e0be1
--- /dev/null
+++ b/playground/samples/anyOf.js
@@ -0,0 +1,37 @@
+module.exports = {
+ schema: {
+ type: "object",
+ properties: {
+ age: {
+ type: "integer",
+ title: "Age",
+ },
+ },
+ anyOf: [
+ {
+ title: "First method of identification",
+ properties: {
+ firstName: {
+ type: "string",
+ title: "First name",
+ default: "Chuck",
+ },
+ lastName: {
+ type: "string",
+ title: "Last name",
+ },
+ },
+ },
+ {
+ title: "Second method of identification",
+ properties: {
+ idCode: {
+ type: "string",
+ title: "ID code",
+ },
+ },
+ },
+ ],
+ },
+ formData: {},
+};
diff --git a/playground/samples/index.js b/playground/samples/index.js
index b496c0fb6d..8ad13741e4 100644
--- a/playground/samples/index.js
+++ b/playground/samples/index.js
@@ -1,4 +1,5 @@
import arrays from "./arrays";
+import anyOf from "./anyOf";
import nested from "./nested";
import numbers from "./numbers";
import simple from "./simple";
@@ -40,4 +41,5 @@ export const samples = {
"Property dependencies": propertyDependencies,
"Schema dependencies": schemaDependencies,
"Additional Properties": additionalProperties,
+ "Optional Forms": anyOf,
};
diff --git a/src/components/fields/AnyOfField.js b/src/components/fields/AnyOfField.js
new file mode 100644
index 0000000000..52fbc74c28
--- /dev/null
+++ b/src/components/fields/AnyOfField.js
@@ -0,0 +1,163 @@
+import React, { Component } from "react";
+import PropTypes from "prop-types";
+import * as types from "../../types";
+import { guessType } from "../../utils";
+import { isValid } from "../../validate";
+
+class AnyOfField extends Component {
+ constructor(props) {
+ super(props);
+
+ const { formData, schema } = this.props;
+
+ this.state = {
+ selectedOption: this.getMatchingOption(formData, schema.anyOf),
+ };
+ }
+
+ componentWillReceiveProps(nextProps) {
+ const matchingOption = this.getMatchingOption(
+ nextProps.formData,
+ nextProps.schema.anyOf
+ );
+
+ if (matchingOption === this.state.selectedOption) {
+ return;
+ }
+
+ this.setState({ selectedOption: matchingOption });
+ }
+
+ getMatchingOption(formData, options) {
+ for (let i = 0; i < options.length; i++) {
+ if (isValid(options[i], formData)) {
+ return i;
+ }
+ }
+
+ // If the form data matches none of the options, use the first option
+ return 0;
+ }
+
+ onOptionChange = event => {
+ const selectedOption = parseInt(event.target.value, 10);
+ const { formData, onChange, schema } = this.props;
+ const options = schema.anyOf;
+
+ if (guessType(formData) === "object") {
+ const newFormData = Object.assign({}, formData);
+
+ const optionsToDiscard = options.slice();
+ optionsToDiscard.splice(selectedOption, 1);
+
+ // Discard any data added using other options
+ for (const option of optionsToDiscard) {
+ if (option.properties) {
+ for (const key in option.properties) {
+ if (newFormData.hasOwnProperty(key)) {
+ delete newFormData[key];
+ }
+ }
+ }
+ }
+
+ onChange(newFormData);
+ } else {
+ onChange(undefined);
+ }
+
+ this.setState({
+ selectedOption: parseInt(event.target.value, 10),
+ });
+ };
+
+ render() {
+ const {
+ disabled,
+ errorSchema,
+ formData,
+ idPrefix,
+ idSchema,
+ onBlur,
+ onChange,
+ onFocus,
+ schema,
+ registry,
+ safeRenderCompletion,
+ uiSchema,
+ } = this.props;
+
+ const _SchemaField = registry.fields.SchemaField;
+ const { selectedOption } = this.state;
+
+ const baseType = schema.type;
+ const options = schema.anyOf || [];
+ const option = options[selectedOption] || null;
+ let optionSchema;
+
+ if (option) {
+ // If the subschema doesn't declare a type, infer the type from the
+ // parent schema
+ optionSchema = option.type
+ ? option
+ : Object.assign({}, option, { type: baseType });
+ }
+
+ return (
+