diff --git a/README.md b/README.md
index 4a1a1a441a87..e22c814fe7b2 100644
--- a/README.md
+++ b/README.md
@@ -62,8 +62,12 @@ Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds
| [
Bryan Robinson](http://bryanlrobinson.com)
[π](https://github.com/netlify/netlify-cms/commits?author=brob "Documentation") | [
Darren](https://github.com/dardub)
[π](https://github.com/netlify/netlify-cms/commits?author=dardub "Documentation") | [
Richard Pullinger](http://www.richardpullinger.com)
[π»](https://github.com/netlify/netlify-cms/commits?author=rpullinger "Code") | [
Taylor Bryant](https://taylorbryant.blog)
[π](https://github.com/netlify/netlify-cms/commits?author=taylorbryant "Documentation") | [
kvanerkelens](https://github.com/kvanerkelens)
[π](https://github.com/netlify/netlify-cms/commits?author=kvanerkelens "Documentation") | [
Patrick Sier](https://patsier.com/)
[π»](https://github.com/netlify/netlify-cms/commits?author=pjsier "Code") | [
Christian Nolte](http://noltech.net)
[π»](https://github.com/netlify/netlify-cms/commits?author=drlogout "Code") |
| [
Edward Betts](http://edwardbetts.com/)
[π](https://github.com/netlify/netlify-cms/commits?author=EdwardBetts "Documentation") | [
Josh Hardman](https://github.com/jhardman0830)
[π](https://github.com/netlify/netlify-cms/commits?author=jhardman0830 "Documentation") | [
Mantas](https://behance.net/mistermantas)
[π](https://github.com/netlify/netlify-cms/commits?author=mistermantas "Documentation") | [
Marco Biedermann](https://www.marcobiedermann.com)
[π](https://github.com/netlify/netlify-cms/commits?author=marcobiedermann "Documentation") | [
Max Stoiber](https://mxstbr.blog/newsletter)
[π](https://github.com/netlify/netlify-cms/commits?author=mxstbr "Documentation") | [
Vyacheslav Matyukhin](http://berekuk.ru)
[π](https://github.com/netlify/netlify-cms/commits?author=berekuk "Documentation") | [
jimmaaay](https://jimmythompson.me)
[π»](https://github.com/netlify/netlify-cms/commits?author=jimmaaay "Code") |
| [
LuΓs Miguel](https://github.com/Quicksaver)
[π](https://github.com/netlify/netlify-cms/issues?q=author%3AQuicksaver "Bug reports") [π»](https://github.com/netlify/netlify-cms/commits?author=Quicksaver "Code") | [
Chris Swithinbank](http://chrisswithinbank.net/)
[π](https://github.com/netlify/netlify-cms/commits?author=delucis "Documentation") | [
remmah](https://github.com/remmah)
[π](https://github.com/netlify/netlify-cms/commits?author=remmah "Documentation") | [
Sumeet Jain](http://sumeetjain.com)
[π](https://github.com/netlify/netlify-cms/commits?author=sumeetjain "Documentation") | [
Sagar Khatri](https://github.com/ragasirtahk)
[π](https://github.com/netlify/netlify-cms/commits?author=ragasirtahk "Documentation") [π‘](#example-ragasirtahk "Examples") | [
Kevin Doocey](https://www.dooceykev.in)
[π»](https://github.com/netlify/netlify-cms/commits?author=Doocey "Code") | [
Swieckowski](https://www.linkedin.com/in/arthur-swieckowski/)
[π»](https://github.com/netlify/netlify-cms/commits?author=Swieckowski "Code") [π](https://github.com/netlify/netlify-cms/commits?author=Swieckowski "Documentation") [β οΈ](https://github.com/netlify/netlify-cms/commits?author=Swieckowski "Tests") |
+<<<<<<< 52b242e65946992775823cd903201b3dbd6fb5be
| [
Tim Carry](http://www.pixelastic.com/)
[π»](https://github.com/netlify/netlify-cms/commits?author=pixelastic "Code") [π¨](#design-pixelastic "Design") [π](https://github.com/netlify/netlify-cms/commits?author=pixelastic "Documentation") | [
Sol Park](https://github.com/solpark)
[π»](https://github.com/netlify/netlify-cms/commits?author=solpark "Code") | [
Michael Romani](https://github.com/michaelromani)
[π»](https://github.com/netlify/netlify-cms/commits?author=michaelromani "Code") | [
Xifeng Jin](http://linkedin/in/xifengjin88)
[π](https://github.com/netlify/netlify-cms/issues?q=author%3Axifengjin88 "Bug reports") [π»](https://github.com/netlify/netlify-cms/commits?author=xifengjin88 "Code") | [
Pedro Duarte](http://pedroduarte.me)
[π](https://github.com/netlify/netlify-cms/issues?q=author%3Apeduarte "Bug reports") [π»](https://github.com/netlify/netlify-cms/commits?author=peduarte "Code") [π](https://github.com/netlify/netlify-cms/commits?author=peduarte "Documentation") | [
Antonio Argote](http://antonioargote.com)
[π¨](#design-Strangehill "Design") | [
Kristaps Taube](https://www.ktaube.com)
[π»](https://github.com/netlify/netlify-cms/commits?author=ktaube "Code") |
| [
David Ko](https://github.com/daveyko)
[π»](https://github.com/netlify/netlify-cms/commits?author=daveyko "Code") | [
IΓ±aki GarcΓa](http://www.txorua.com)
[π¨](#design-igarbla "Design") |
+=======
+| [
Tim Carry](http://www.pixelastic.com/)
[π»](https://github.com/netlify/netlify-cms/commits?author=pixelastic "Code") [π¨](#design-pixelastic "Design") [π](https://github.com/netlify/netlify-cms/commits?author=pixelastic "Documentation") | [
Sol Park](https://github.com/solpark)
[π»](https://github.com/netlify/netlify-cms/commits?author=solpark "Code") | [
Michael Romani](https://github.com/michaelromani)
[π»](https://github.com/netlify/netlify-cms/commits?author=michaelromani "Code") | [
Alex Moon](https://github.com/moonmeister)
[π»](https://github.com/netlify/netlify-cms/commits?author=moonmeister "Code") [π](https://github.com/netlify/netlify-cms/commits?author=moonmeister "Documentation") |
+>>>>>>> add self as contributer
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
diff --git a/example/config.yml b/example/config.yml
index 9e245c33c788..a282f7b9cb6d 100644
--- a/example/config.yml
+++ b/example/config.yml
@@ -68,6 +68,14 @@ collections: # A list of collections the CMS should be able to edit
folder: "_sink"
create: true
fields:
+ - label: "Tags"
+ name: "tags"
+ widget: "relations"
+ limit: "3"
+ collection: "posts"
+ searchFields: ["title"]
+ valueField: "title"
+
- label: "Related Post"
name: "post"
widget: "relationKitchenSinkPost"
diff --git a/package.json b/package.json
index 411396bc65fc..4a0adacdb0ec 100644
--- a/package.json
+++ b/package.json
@@ -154,6 +154,7 @@
"react-dom": "^16.0.0",
"react-frame-component": "^2.0.0",
"react-immutable-proptypes": "^2.1.0",
+ "react-input-autosize": "^2.2.1",
"react-modal": "^3.1.5",
"react-redux": "^4.4.0",
"react-router-dom": "^4.2.2",
@@ -161,6 +162,7 @@
"react-scroll-sync": "^0.4.0",
"react-sortable-hoc": "^0.6.8",
"react-split-pane": "^0.1.66",
+ "react-tagsinput": "^3.19.0",
"react-textarea-autosize": "^5.2.0",
"react-toggled": "^1.1.2",
"react-topbar-progress-indicator": "^2.0.0",
diff --git a/src/components/EditorWidgets/EditorWidgets.css b/src/components/EditorWidgets/EditorWidgets.css
index 1c7eec76c7ba..05fc5a55ea55 100644
--- a/src/components/EditorWidgets/EditorWidgets.css
+++ b/src/components/EditorWidgets/EditorWidgets.css
@@ -7,6 +7,7 @@
@import "./Boolean/Boolean.css";
@import "./Relation/Relation.css";
@import "./DateTime/DateTime.css";
+@import "./Relations/Relations.css";
:root {
--widgetNestDistance: 14px;
diff --git a/src/components/EditorWidgets/Relations/Relations.css b/src/components/EditorWidgets/Relations/Relations.css
new file mode 100644
index 000000000000..03cfab850911
--- /dev/null
+++ b/src/components/EditorWidgets/Relations/Relations.css
@@ -0,0 +1,42 @@
+@import '../Relation/ReactAutosuggest.css';
+
+.react-tagsinput-tag {
+ border: 1px solid var(--colorActive);
+ border-radius: var(--borderWidth);
+ display: inline-block;
+ background-color: var(--colorBackground);
+ margin: 2.5px;
+ padding: 5px;
+ color: var(--colorTextLead);
+ font-size: 13px;
+ font-family: var(--fontFamilyMono);
+ font-weight: 400;
+}
+
+.react-tagsinput-remove {
+ font-weight: bold;
+ cursor: pointer;
+}
+
+.react-tagsinput-tag a::before {
+ content: ' Γ';
+}
+
+.react-tagsinput-input {
+ border: 0;
+ margin-top: auto;
+ margin-bottom: auto;
+ background: var(--colorInputBackground);
+ padding: 5px;
+ outline: none;
+ width: auto;
+ color: var(--colorTextLead);
+ font-size: 13px;
+ font-weight: 400;
+ font-family: var(--fontFamilyPrimary);
+}
+
+.nc-relations-container {
+ display: inline-block;
+ width: auto;
+}
diff --git a/src/components/EditorWidgets/Relations/RelationsControl.js b/src/components/EditorWidgets/Relations/RelationsControl.js
new file mode 100644
index 000000000000..d74647eb8a44
--- /dev/null
+++ b/src/components/EditorWidgets/Relations/RelationsControl.js
@@ -0,0 +1,171 @@
+import React, { Component } from 'react';
+import { Loader } from 'UI';
+import TagsInput from 'react-tagsinput';
+import PropTypes from 'prop-types';
+import Autosuggest from 'react-autosuggest';
+import uuid from 'uuid/v4';
+import { Map } from 'immutable';
+import { connect } from 'react-redux';
+import { debounce } from 'lodash';
+import { query, clearSearch } from 'Actions/search';
+
+class RelationsControl extends Component {
+ static propTypes = {
+ onChange: PropTypes.func.isRequired,
+ forID: PropTypes.string,
+ value: PropTypes.array,
+ field: PropTypes.node,
+ isFetching: PropTypes.node,
+ query: PropTypes.func.isRequired,
+ clearSearch: PropTypes.func.isRequired,
+ queryHits: PropTypes.oneOfType([
+ PropTypes.array,
+ PropTypes.object,
+ ]),
+ classNameWrapper: PropTypes.string.isRequired,
+ setActiveStyle: PropTypes.func.isRequired,
+ setInactiveStyle: PropTypes.func.isRequired,
+ };
+
+ constructor(props, ctx) {
+ super(props, ctx);
+ this.controlID = uuid();
+ this.didInitialSearch = false;
+ }
+
+ componentDidMount() {
+ const { value, field } = this.props;
+ if (value) {
+ const collection = field.get('collection');
+ const searchFields = field.get('searchFields').toJS();
+ this.props.query(this.controlID, collection, searchFields, value);
+ }
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (this.didInitialSearch) return;
+ if (nextProps.queryHits !== this.props.queryHits && nextProps.queryHits.get && nextProps.queryHits.get(this.controlID)) {
+ this.didInitialSearch = true;
+ const suggestion = nextProps.queryHits.get(this.controlID);
+ if (suggestion && suggestion.length === 1) {
+ const val = this.getSuggestionValue(suggestion[0]);
+ this.props.onChange(val, { [nextProps.field.get('collection')]: { [val]: suggestion[0].data } });
+ }
+ }
+ }
+
+ onChange = (relations) => {
+ this.props.onChange(relations.map(val => val));
+ };
+
+ onSuggestionsFetchRequested = debounce(({ value }) => {
+ if (value.length < 2) return;
+ const { field } = this.props;
+ const collection = field.get('collection');
+ const searchFields = field.get('searchFields').toJS();
+ this.props.query(this.controlID, collection, searchFields, value);
+ }, 500);
+
+ onSuggestionsClearRequested = () => {
+ this.props.clearSearch();
+ };
+
+ getSuggestionValue = (suggestion) => {
+ const { field } = this.props;
+ const valueField = field.get('valueField');
+ return suggestion.data[valueField];
+ };
+
+ autocompleteRenderInput = ({ addTag, ...props }) => {
+ const handleOnChange = (e, { newValue, method }) => {
+ if (method === 'enter') {
+ e.preventDefault();
+ } else {
+ this.onChange(e);
+ }
+ };
+
+ const inputprops = {
+ placeholder: props.placeholder,
+ onChange: handleOnChange,
+ };
+
+ const suggestions = (this.props.queryHits.get) ? this.props.queryHits.get(this.controlID, []) : [];
+
+ return (
+