diff --git a/dist/js/gutenberg.js b/dist/js/gutenberg.js
index 538d84802de8aac982c4f024d4b4847e34750746..c692e7f368fec3fea065e8aa5ff57294c6ce711c 100644
--- a/dist/js/gutenberg.js
+++ b/dist/js/gutenberg.js
@@ -1 +1,2 @@
-!function(){var o,r={194:function(o){"use strict";o.exports=wp.i18n}},t={};function n(o){var e=t[o];if(void 0!==e)return e.exports;var s=t[o]={exports:{}};return r[o](s,s.exports,n),s.exports}o=n(194).__,console.log(o("Hello, world","wp_revisions_control"))}();
\ No newline at end of file
+/*! For license information please see gutenberg.js.LICENSE.txt */
+!function(){"use strict";var e={418:function(e){var t=Object.getOwnPropertySymbols,r=Object.prototype.hasOwnProperty,n=Object.prototype.propertyIsEnumerable;function o(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},r=0;r<10;r++)t["_"+String.fromCharCode(r)]=r;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var n={};return"abcdefghijklmnopqrst".split("").forEach((function(e){n[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},n)).join("")}catch(e){return!1}}()?Object.assign:function(e,i){for(var c,a,u=o(e),s=1;s<arguments.length;s++){for(var p in c=Object(arguments[s]))r.call(c,p)&&(u[p]=c[p]);if(t){a=t(c);for(var f=0;f<a.length;f++)n.call(c,a[f])&&(u[a[f]]=c[a[f]])}}return u}},251:function(e,t,r){r(418);var n=r(804),o=60103;if(60107,"function"==typeof Symbol&&Symbol.for){var i=Symbol.for;o=i("react.element"),i("react.fragment")}var c=n.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,a=Object.prototype.hasOwnProperty,u={key:!0,ref:!0,__self:!0,__source:!0};function s(e,t,r){var n,i={},s=null,p=null;for(n in void 0!==r&&(s=""+r),void 0!==t.key&&(s=""+t.key),void 0!==t.ref&&(p=t.ref),t)a.call(t,n)&&!u.hasOwnProperty(n)&&(i[n]=t[n]);if(e&&e.defaultProps)for(n in t=e.defaultProps)void 0===i[n]&&(i[n]=t[n]);return{$$typeof:o,type:e,key:s,ref:p,props:i,_owner:c.current}}t.jsx=s},893:function(e,t,r){e.exports=r(251)},804:function(e){e.exports=React},190:function(e){e.exports=wp.components},405:function(e){e.exports=wp.compose},440:function(e){e.exports=wp.data},451:function(e){e.exports=wp.editPost},194:function(e){e.exports=wp.i18n},415:function(e){e.exports=wp.plugins}},t={};function r(n){var o=t[n];if(void 0!==o)return o.exports;var i=t[n]={exports:{}};return e[n](i,i.exports,r),i.exports}!function(){var e=r(893);function t(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}var n=r(190).TextControl,o=r(405).compose,i=r(440),c=i.withSelect,a=i.withDispatch,u=r(451).PluginDocumentSettingPanel,s=r(194).__,p=r(415).registerPlugin,f="_wp_rev_ctl_limit";p("plugin-document-setting-panel-demo",{render:o([c((function(e){return{limit:e("core/editor").getEditedPostAttribute("meta")[f]}})),a((function(e){return{update:function(r){e("core/editor").editPost({meta:t({},f,r)})}}}))])((function(t){var r=t.limit,o=t.update;return(0,e.jsx)(u,{name:"wp-revisions-control",title:s("WP Revisions Control","wp_revisions_control"),className:"wp-revisions-control",children:(0,e.jsx)(n,{label:s("Number of revisions to retain.","wp_revisions_control"),value:r,onChange:o})})})),icon:"backup"})}()}();
\ No newline at end of file
diff --git a/inc/class-wp-revisions-control.php b/inc/class-wp-revisions-control.php
index 87b9ef653ec71ac4a8df1ce042b36cbb7e20ae3a..fabaa513dd0c7c016c4d739df68c793565ed36db 100644
--- a/inc/class-wp-revisions-control.php
+++ b/inc/class-wp-revisions-control.php
@@ -181,7 +181,12 @@ class WP_Revisions_Control {
 				__DIR__
 			),
 			array(
+				'wp-components',
+				'wp-compose',
+				'wp-data',
+				'wp-edit-post',
 				'wp-i18n',
+				'wp-plugins',
 			),
 			2021032701
 		);
diff --git a/languages/wp-revisions-control-gutenberg.pot b/languages/wp-revisions-control-gutenberg.pot
index 4dead06726e5e952a08d107576dbc205c26b68ed..7f2fe3aa3310ef1a6747260bcc3d832159dedcf9 100644
--- a/languages/wp-revisions-control-gutenberg.pot
+++ b/languages/wp-revisions-control-gutenberg.pot
@@ -3,6 +3,10 @@ msgstr ""
 "Content-Type: text/plain; charset=utf-8\n"
 "X-Generator: babel-plugin-makepot\n"
 
-#: src/js/gutenberg.js:3
-msgid "Hello, world"
+#: src/js/gutenberg.js:13
+msgid "WP Revisions Control"
+msgstr ""
+
+#: src/js/gutenberg.js:17
+msgid "Number of revisions to retain."
 msgstr ""
\ No newline at end of file
diff --git a/languages/wp-revisions-control.pot b/languages/wp-revisions-control.pot
index f01dca7b3a118c67b5abc072dd2e2b226c994fd5..078f79b06e461f4f18d19d739dd1863a14ed29c4 100644
--- a/languages/wp-revisions-control.pot
+++ b/languages/wp-revisions-control.pot
@@ -5,7 +5,7 @@ msgstr ""
 "Project-Id-Version: WP Revisions Control 1.3\n"
 "Report-Msgid-Bugs-To: "
 "https://wordpress.org/support/plugin/wp-revisions-control\n"
-"POT-Creation-Date: 2021-03-27 21:26:05+00:00\n"
+"POT-Creation-Date: 2021-03-28 01:34:41+00:00\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=utf-8\n"
 "Content-Transfer-Encoding: 8bit\n"
@@ -45,17 +45,21 @@ msgstr ""
 msgid "WP Revisions Control encountered an unspecified error."
 msgstr ""
 
-#: inc/class-wp-revisions-control.php:158
+#: inc/class-wp-revisions-control.php:138
+msgid "Number of revisions to retain."
+msgstr ""
+
+#: inc/class-wp-revisions-control.php:205
 msgid ""
 "Set the number of revisions to save for each post type listed. To retain "
 "all revisions for a given post type, leave the field empty."
 msgstr ""
 
-#: inc/class-wp-revisions-control.php:159
+#: inc/class-wp-revisions-control.php:206
 msgid "If a post type isn't listed, revisions are not enabled for that post type."
 msgstr ""
 
-#: inc/class-wp-revisions-control.php:170
+#: inc/class-wp-revisions-control.php:217
 #. translators: 1. Filter tag.
 msgid ""
 "A local change is causing this plugin's functionality to run at a priority "
@@ -63,60 +67,60 @@ msgid ""
 "please unhook any functions from the %1$s filter."
 msgstr ""
 
-#: inc/class-wp-revisions-control.php:287
+#: inc/class-wp-revisions-control.php:342
 msgid "Revisions"
 msgstr ""
 
-#: inc/class-wp-revisions-control.php:309
+#: inc/class-wp-revisions-control.php:375
 msgid "Processing&hellip;"
 msgstr ""
 
-#: inc/class-wp-revisions-control.php:310
+#: inc/class-wp-revisions-control.php:376
 msgid "Are you sure you want to remove revisions from this post?"
 msgstr ""
 
-#: inc/class-wp-revisions-control.php:311
+#: inc/class-wp-revisions-control.php:377
 msgid "Autosave"
 msgstr ""
 
-#: inc/class-wp-revisions-control.php:312
+#: inc/class-wp-revisions-control.php:378
 msgid "There are no revisions to remove."
 msgstr ""
 
-#: inc/class-wp-revisions-control.php:313
+#: inc/class-wp-revisions-control.php:379
 msgid "An error occurred. Please refresh the page and try again."
 msgstr ""
 
-#: inc/class-wp-revisions-control.php:334
+#: inc/class-wp-revisions-control.php:399
 msgid "Purge these revisions"
 msgstr ""
 
-#: inc/class-wp-revisions-control.php:340
+#: inc/class-wp-revisions-control.php:405
 #. translators: 1. Text input field.
 msgid ""
 "Limit this post to %1$s revisions. Leave this field blank for default "
 "behavior."
 msgstr ""
 
-#: inc/class-wp-revisions-control.php:365
+#: inc/class-wp-revisions-control.php:430
 msgid "No post ID was provided. Please refresh the page and try again."
 msgstr ""
 
-#: inc/class-wp-revisions-control.php:367
+#: inc/class-wp-revisions-control.php:432
 msgid "Invalid request. Please refresh the page and try again."
 msgstr ""
 
-#: inc/class-wp-revisions-control.php:369
+#: inc/class-wp-revisions-control.php:434
 msgid "You are not allowed to edit this post."
 msgstr ""
 
-#: inc/class-wp-revisions-control.php:401
+#: inc/class-wp-revisions-control.php:466
 #. translators: 1. Number of removed revisions, already formatted for locale.
 msgid "Removed %1$s revisions associated with this post."
 msgstr ""
 
-#: inc/class-wp-revisions-control.php:427
-#: inc/class-wp-revisions-control.php:439
+#: inc/class-wp-revisions-control.php:492
+#: inc/class-wp-revisions-control.php:504
 msgid "No revisions to remove."
 msgstr ""
 
diff --git a/package-lock.json b/package-lock.json
index 95e1cb4362c732083061e2fffd4eaac1831c22b8..01fdd1454ff9aa896c4cfdd93c40f85eff5d3d2d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,7 +12,9 @@
         "grunt": "~0.4.5",
         "grunt-wp-i18n": "~0.5.0",
         "grunt-wp-readme-to-markdown": "~1.0.0",
-        "laravel-mix": "^6.0.13"
+        "laravel-mix": "^6.0.13",
+        "react": "^17.0.2",
+        "react-dom": "^17.0.2"
       }
     },
     "node_modules/@babel/code-frame": {
@@ -8086,6 +8088,18 @@
       "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
       "dev": true
     },
+    "node_modules/loose-envify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+      "dev": true,
+      "dependencies": {
+        "js-tokens": "^3.0.0 || ^4.0.0"
+      },
+      "bin": {
+        "loose-envify": "cli.js"
+      }
+    },
     "node_modules/lower-case": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
@@ -13131,6 +13145,33 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/react": {
+      "version": "17.0.2",
+      "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
+      "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
+      "dev": true,
+      "dependencies": {
+        "loose-envify": "^1.1.0",
+        "object-assign": "^4.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/react-dom": {
+      "version": "17.0.2",
+      "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
+      "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
+      "dev": true,
+      "dependencies": {
+        "loose-envify": "^1.1.0",
+        "object-assign": "^4.1.1",
+        "scheduler": "^0.20.2"
+      },
+      "peerDependencies": {
+        "react": "17.0.2"
+      }
+    },
     "node_modules/readable-stream": {
       "version": "2.3.7",
       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
@@ -13526,6 +13567,16 @@
       "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
       "dev": true
     },
+    "node_modules/scheduler": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
+      "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
+      "dev": true,
+      "dependencies": {
+        "loose-envify": "^1.1.0",
+        "object-assign": "^4.1.1"
+      }
+    },
     "node_modules/schema-utils": {
       "version": "2.7.1",
       "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
@@ -22378,6 +22429,15 @@
       "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
       "dev": true
     },
+    "loose-envify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+      "dev": true,
+      "requires": {
+        "js-tokens": "^3.0.0 || ^4.0.0"
+      }
+    },
     "lower-case": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
@@ -26333,6 +26393,27 @@
         }
       }
     },
+    "react": {
+      "version": "17.0.2",
+      "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
+      "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
+      "dev": true,
+      "requires": {
+        "loose-envify": "^1.1.0",
+        "object-assign": "^4.1.1"
+      }
+    },
+    "react-dom": {
+      "version": "17.0.2",
+      "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
+      "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
+      "dev": true,
+      "requires": {
+        "loose-envify": "^1.1.0",
+        "object-assign": "^4.1.1",
+        "scheduler": "^0.20.2"
+      }
+    },
     "readable-stream": {
       "version": "2.3.7",
       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
@@ -26653,6 +26734,16 @@
       "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
       "dev": true
     },
+    "scheduler": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
+      "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
+      "dev": true,
+      "requires": {
+        "loose-envify": "^1.1.0",
+        "object-assign": "^4.1.1"
+      }
+    },
     "schema-utils": {
       "version": "2.7.1",
       "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
diff --git a/package.json b/package.json
index ce5ced7215af5e349762fac733af902441f69c10..e2f583e3349a36816e385cce654c81e61c638877 100755
--- a/package.json
+++ b/package.json
@@ -10,7 +10,9 @@
     "grunt": "~0.4.5",
     "grunt-wp-i18n": "~0.5.0",
     "grunt-wp-readme-to-markdown": "~1.0.0",
-    "laravel-mix": "^6.0.13"
+    "laravel-mix": "^6.0.13",
+    "react": "^17.0.2",
+    "react-dom": "^17.0.2"
   },
   "scripts": {
     "dev": "NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --config=node_modules/laravel-mix/setup/webpack.config.js",
diff --git a/src/js/gutenberg.js b/src/js/gutenberg.js
index f6136ff9f8f5dbf41a6286a583a6d8849aeb959b..d44988de675890534f4cffdb7b2afa089b907025 100644
--- a/src/js/gutenberg.js
+++ b/src/js/gutenberg.js
@@ -1,3 +1,57 @@
+const { TextControl } = require( '@wordpress/components' );
+const { compose } = require( '@wordpress/compose' );
+const { withSelect, withDispatch } = require( '@wordpress/data' );
+const { PluginDocumentSettingPanel } = require( '@wordpress/edit-post' );
 const { __ } = require( '@wordpress/i18n' );
+const { registerPlugin } = require( '@wordpress/plugins' );
 
-console.log( __( 'Hello, world', 'wp_revisions_control' ) );
+const metaKey = '_wp_rev_ctl_limit';
+
+const Render = ( { limit, update } ) => (
+	<PluginDocumentSettingPanel
+		name="wp-revisions-control"
+		title={ __( 'WP Revisions Control', 'wp_revisions_control' ) }
+		className="wp-revisions-control"
+	>
+		<TextControl
+			label={ __( 'Number of revisions to retain.', 'wp_revisions_control' ) }
+			value={ limit }
+			onChange={ update }
+		/>
+	</PluginDocumentSettingPanel>
+);
+
+const RevisionsControl = compose(
+	[
+		withSelect( ( select ) => {
+			const limit = select( 'core/editor' ).getEditedPostAttribute(
+				'meta'
+			)[ metaKey ];
+
+			return {
+				limit,
+			};
+		} ),
+		withDispatch( ( dispatch ) => {
+			const update = ( value ) => {
+				dispatch( 'core/editor' ).editPost( {
+					meta: {
+						[ metaKey ]: value,
+					},
+				} );
+			};
+
+			return {
+				update,
+			};
+		} ),
+	]
+)( Render );
+
+registerPlugin(
+	'plugin-document-setting-panel-demo',
+	{
+		render: RevisionsControl,
+		icon: 'backup',
+	}
+);
diff --git a/webpack.mix.js b/webpack.mix.js
index 95e1a2b8efee2a0609b5e99abcec9cd5d64ca958..95aa461b4a50ccf5978ecf8c620246ac1e05cb1c 100644
--- a/webpack.mix.js
+++ b/webpack.mix.js
@@ -15,7 +15,12 @@ mix.autoload( {} )
 	} )
 	.webpackConfig( {
 		externals: {
+			'@wordpress/components': 'wp.components',
+			'@wordpress/compose': 'wp.compose',
+			'@wordpress/data': 'wp.data',
+			'@wordpress/edit-post': 'wp.editPost',
 			'@wordpress/i18n': 'wp.i18n',
+			'@wordpress/plugins': 'wp.plugins',
 			'react': 'React',
 			'react-dom': 'ReactDOM',
 		}