diff --git a/README.md b/README.md
index f6cd505a54..651bf6956e 100644
--- a/README.md
+++ b/README.md
@@ -168,6 +168,7 @@ Enable the rules that you would like to use.
* [react/jsx-no-comment-textnodes](docs/rules/jsx-no-comment-textnodes.md): Prevent comments from being inserted as text nodes
* [react/jsx-no-duplicate-props](docs/rules/jsx-no-duplicate-props.md): Prevent duplicate props in JSX
* [react/jsx-no-literals](docs/rules/jsx-no-literals.md): Prevent usage of unwrapped JSX strings
+* [react/jsx-no-script-url](docs/rules/jsx-no-script-url.md): Prevent usage of `javascript:` URLs
* [react/jsx-no-target-blank](docs/rules/jsx-no-target-blank.md): Prevent usage of unsafe `target='_blank'`
* [react/jsx-no-undef](docs/rules/jsx-no-undef.md): Disallow undeclared variables in JSX
* [react/jsx-no-useless-fragment](docs/rules/jsx-no-useless-fragment.md): Disallow unnescessary fragments (fixable)
diff --git a/docs/rules/jsx-no-script-url.md b/docs/rules/jsx-no-script-url.md
new file mode 100644
index 0000000000..53f9202ef4
--- /dev/null
+++ b/docs/rules/jsx-no-script-url.md
@@ -0,0 +1,22 @@
+# Prevent usage of `javascript:` URLs (react/jsx-no-script-url)
+
+**In React 16.9** any URLs starting with `javascript:` [scheme](https://wiki.whatwg.org/wiki/URL_schemes#javascript:_URLs) log a warning.
+React considers the pattern as a dangerous attack surface, see [details](https://reactjs.org/blog/2019/08/08/react-v16.9.0.html#deprecating-javascript-urls).
+**In a future major release**, React will throw an error if it encounters a `javascript:` URL.
+
+## Rule Details
+
+The following patterns are considered warnings:
+
+```jsx
+
+
+
+```
+
+The following patterns are **not** considered warnings:
+
+```jsx
+
+
+```
diff --git a/index.js b/index.js
index c9511bb1e3..6db76d0aee 100644
--- a/index.js
+++ b/index.js
@@ -34,6 +34,7 @@ const allRules = {
'jsx-no-comment-textnodes': require('./lib/rules/jsx-no-comment-textnodes'),
'jsx-no-duplicate-props': require('./lib/rules/jsx-no-duplicate-props'),
'jsx-no-literals': require('./lib/rules/jsx-no-literals'),
+ 'jsx-no-script-url': require('./lib/rules/jsx-no-script-url'),
'jsx-no-target-blank': require('./lib/rules/jsx-no-target-blank'),
'jsx-no-useless-fragment': require('./lib/rules/jsx-no-useless-fragment'),
'jsx-one-expression-per-line': require('./lib/rules/jsx-one-expression-per-line'),
diff --git a/lib/rules/jsx-no-script-url.js b/lib/rules/jsx-no-script-url.js
new file mode 100644
index 0000000000..480294c2a3
--- /dev/null
+++ b/lib/rules/jsx-no-script-url.js
@@ -0,0 +1,48 @@
+/**
+ * @fileoverview Prevent usage of `javascript:` URLs
+ * @author Sergei Startsev
+ */
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+function isHref(attr) {
+ return attr.name &&
+ attr.name.name === 'href';
+}
+
+// https://github.com/facebook/react/blob/d0ebde77f6d1232cefc0da184d731943d78e86f2/packages/react-dom/src/shared/sanitizeURL.js#L30
+/* eslint-disable-next-line max-len, no-control-regex */
+const isJavaScriptProtocol = /^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*\:/i;
+
+function hasJavaScriptProtocol(attr) {
+ return attr.value.type === 'Literal' &&
+ isJavaScriptProtocol.test(attr.value.value);
+}
+
+module.exports = {
+ meta: {
+ docs: {
+ description: 'Forbid `javascript:` URLs',
+ category: 'Best Practices',
+ recommended: false,
+ url: docsUrl('jsx-no-script-url')
+ },
+ schema: []
+ },
+
+ create: function (context) {
+ return {
+ JSXAttribute: function (node) {
+ if (node.parent.name.name === 'a' && isHref(node) && hasJavaScriptProtocol(node)) {
+ context.report(node, 'A future version of React will block javascript: URLs as a security precaution. ' +
+ 'Use event handlers instead if you can. If you need to generate unsafe HTML try using dangerouslySetInnerHTML instead.');
+ }
+ }
+ };
+ }
+};
diff --git a/tests/lib/rules/jsx-no-script-url.js b/tests/lib/rules/jsx-no-script-url.js
new file mode 100644
index 0000000000..f81017aed9
--- /dev/null
+++ b/tests/lib/rules/jsx-no-script-url.js
@@ -0,0 +1,52 @@
+/**
+ * @fileoverview Prevent usage of `javascript:` URLs
+ * @author Sergei Startsev
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/jsx-no-script-url');
+const RuleTester = require('eslint').RuleTester;
+
+const parserOptions = {
+ ecmaVersion: 2018,
+ sourceType: 'module',
+ ecmaFeatures: {
+ jsx: true
+ }
+};
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({parserOptions});
+const defaultErrors = [{
+ message: 'A future version of React will block javascript: URLs as a security precaution. ' +
+ 'Use event handlers instead if you can. If you need to generate unsafe HTML try using dangerouslySetInnerHTML instead.'
+}];
+
+ruleTester.run('jsx-no-script-url', rule, {
+ valid: [
+ {code: ''},
+ {code: ''},
+ {code: ''},
+ {code: ''},
+ {code: ''},
+ {code: ''},
+ {code: ''}
+ ],
+ invalid: [{
+ code: '',
+ errors: defaultErrors
+ }, {
+ code: '',
+ errors: defaultErrors
+ }, {
+ code: '',
+ errors: defaultErrors
+ }]
+});