diff --git a/package-lock.json b/package-lock.json
index db2d55f6..6a2cfeb7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -54,7 +54,7 @@
         "raf": "3.4.1",
         "react": "16.14.0",
         "react-dom": "16.14.0",
-        "react-intl": "2.9.0",
+        "react-intl": "^6.6.8",
         "react-intl-redux": "2.4.1",
         "react-popover": "0.5.10",
         "react-redux": "5.1.2",
@@ -83,7 +83,7 @@
       "peerDependencies": {
         "react": "^16",
         "react-dom": "^16",
-        "react-intl": "^2",
+        "react-intl": "^6",
         "react-intl-redux": "^0.7 || ^2.0.0",
         "react-popover": "^0.5",
         "react-redux": "^5",
@@ -2829,6 +2829,100 @@
         "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
       }
     },
+    "node_modules/@formatjs/ecma402-abstract": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz",
+      "integrity": "sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==",
+      "dev": true,
+      "dependencies": {
+        "@formatjs/intl-localematcher": "0.5.4",
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@formatjs/fast-memoize": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz",
+      "integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==",
+      "dev": true,
+      "dependencies": {
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@formatjs/icu-messageformat-parser": {
+      "version": "2.7.8",
+      "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.8.tgz",
+      "integrity": "sha512-nBZJYmhpcSX0WeJ5SDYUkZ42AgR3xiyhNCsQweFx3cz/ULJjym8bHAzWKvG5e2+1XO98dBYC0fWeeAECAVSwLA==",
+      "dev": true,
+      "dependencies": {
+        "@formatjs/ecma402-abstract": "2.0.0",
+        "@formatjs/icu-skeleton-parser": "1.8.2",
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@formatjs/icu-skeleton-parser": {
+      "version": "1.8.2",
+      "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.2.tgz",
+      "integrity": "sha512-k4ERKgw7aKGWJZgTarIcNEmvyTVD9FYh0mTrrBMHZ1b8hUu6iOJ4SzsZlo3UNAvHYa+PnvntIwRPt1/vy4nA9Q==",
+      "dev": true,
+      "dependencies": {
+        "@formatjs/ecma402-abstract": "2.0.0",
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@formatjs/intl": {
+      "version": "2.10.4",
+      "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.10.4.tgz",
+      "integrity": "sha512-56483O+HVcL0c7VucAS2tyH020mt9XTozZO67cwtGg0a7KWDukS/FzW3OnvaHmTHDuYsoPIzO+ZHVfU6fT/bJw==",
+      "dev": true,
+      "dependencies": {
+        "@formatjs/ecma402-abstract": "2.0.0",
+        "@formatjs/fast-memoize": "2.2.0",
+        "@formatjs/icu-messageformat-parser": "2.7.8",
+        "@formatjs/intl-displaynames": "6.6.8",
+        "@formatjs/intl-listformat": "7.5.7",
+        "intl-messageformat": "10.5.14",
+        "tslib": "^2.4.0"
+      },
+      "peerDependencies": {
+        "typescript": "^4.7 || 5"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@formatjs/intl-displaynames": {
+      "version": "6.6.8",
+      "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.6.8.tgz",
+      "integrity": "sha512-Lgx6n5KxN16B3Pb05z3NLEBQkGoXnGjkTBNCZI+Cn17YjHJ3fhCeEJJUqRlIZmJdmaXQhjcQVDp6WIiNeRYT5g==",
+      "dev": true,
+      "dependencies": {
+        "@formatjs/ecma402-abstract": "2.0.0",
+        "@formatjs/intl-localematcher": "0.5.4",
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@formatjs/intl-listformat": {
+      "version": "7.5.7",
+      "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.5.7.tgz",
+      "integrity": "sha512-MG2TSChQJQT9f7Rlv+eXwUFiG24mKSzmF144PLb8m8OixyXqn4+YWU+5wZracZGCgVTVmx8viCf7IH3QXoiB2g==",
+      "dev": true,
+      "dependencies": {
+        "@formatjs/ecma402-abstract": "2.0.0",
+        "@formatjs/intl-localematcher": "0.5.4",
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@formatjs/intl-localematcher": {
+      "version": "0.5.4",
+      "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz",
+      "integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==",
+      "dev": true,
+      "dependencies": {
+        "tslib": "^2.4.0"
+      }
+    },
     "node_modules/@humanwhocodes/config-array": {
       "version": "0.11.14",
       "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
@@ -5482,6 +5576,16 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/hoist-non-react-statics": {
+      "version": "3.3.5",
+      "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz",
+      "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==",
+      "dev": true,
+      "dependencies": {
+        "@types/react": "*",
+        "hoist-non-react-statics": "^3.3.0"
+      }
+    },
     "node_modules/@types/istanbul-lib-coverage": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
@@ -5565,6 +5669,22 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/@types/prop-types": {
+      "version": "15.7.12",
+      "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
+      "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==",
+      "dev": true
+    },
+    "node_modules/@types/react": {
+      "version": "18.3.6",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.6.tgz",
+      "integrity": "sha512-CnGaRYNu2iZlkGXGrOYtdg5mLK8neySj0woZ4e2wF/eli2E6Sazmq5X+Nrj6OBrrFVQfJWTUFeqAzoRhWQXYvg==",
+      "dev": true,
+      "dependencies": {
+        "@types/prop-types": "*",
+        "csstype": "^3.0.2"
+      }
+    },
     "node_modules/@types/stack-utils": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
@@ -9183,6 +9303,12 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/csstype": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+      "dev": true
+    },
     "node_modules/cyclist": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.2.tgz",
@@ -14965,21 +15091,16 @@
         "node": ">= 0.10"
       }
     },
-    "node_modules/intl-format-cache": {
-      "version": "2.2.9",
-      "resolved": "https://registry.npmjs.org/intl-format-cache/-/intl-format-cache-2.2.9.tgz",
-      "integrity": "sha512-Zv/u8wRpekckv0cLkwpVdABYST4hZNTDaX7reFetrYTJwxExR2VyTqQm+l0WmL0Qo8Mjb9Tf33qnfj0T7pjxdQ==",
-      "dev": true,
-      "license": "BSD-3-Clause"
-    },
     "node_modules/intl-messageformat": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-2.2.0.tgz",
-      "integrity": "sha512-I+tSvHnXqJYjDfNmY95tpFMj30yoakC6OXAo+wu/wTMy6tA/4Fd4mvV7Uzs4cqK/Ap29sHhwjcY+78a8eifcXw==",
+      "version": "10.5.14",
+      "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.14.tgz",
+      "integrity": "sha512-IjC6sI0X7YRjjyVH9aUgdftcmZK7WXdHeil4KwbjDnRWjnVitKpAx3rr6t6di1joFp5188VqKcobOPA6mCLG/w==",
       "dev": true,
-      "license": "BSD-3-Clause",
       "dependencies": {
-        "intl-messageformat-parser": "1.4.0"
+        "@formatjs/ecma402-abstract": "2.0.0",
+        "@formatjs/fast-memoize": "2.2.0",
+        "@formatjs/icu-messageformat-parser": "2.7.8",
+        "tslib": "^2.4.0"
       }
     },
     "node_modules/intl-messageformat-parser": {
@@ -14990,25 +15111,6 @@
       "dev": true,
       "license": "BSD-3-Clause"
     },
-    "node_modules/intl-messageformat/node_modules/intl-messageformat-parser": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-1.4.0.tgz",
-      "integrity": "sha512-/XkqFHKezO6UcF4Av2/Lzfrez18R0jyw7kRFhSeB/YRakdrgSc9QfFZUwNJI9swMwMoNPygK1ArC5wdFSjPw+A==",
-      "deprecated": "We've written a new parser that's 6x faster and is backwards compatible. Please use @formatjs/icu-messageformat-parser",
-      "dev": true,
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/intl-relativeformat": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/intl-relativeformat/-/intl-relativeformat-2.2.0.tgz",
-      "integrity": "sha512-4bV/7kSKaPEmu6ArxXf9xjv1ny74Zkwuey8Pm01NH4zggPP7JHwg2STk8Y3JdspCKRDriwIyLRfEXnj2ZLr4Bw==",
-      "deprecated": "This package has been deprecated, please see migration guide at 'https://github.com/formatjs/formatjs/tree/master/packages/intl-relativeformat#migration-guide'",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "intl-messageformat": "^2.0.0"
-      }
-    },
     "node_modules/into-stream": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz",
@@ -26408,21 +26510,30 @@
       }
     },
     "node_modules/react-intl": {
-      "version": "2.9.0",
-      "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-2.9.0.tgz",
-      "integrity": "sha512-27jnDlb/d2A7mSJwrbOBnUgD+rPep+abmoJE511Tf8BnoONIAUehy/U1zZCHGO17mnOwMWxqN4qC0nW11cD6rA==",
+      "version": "6.6.8",
+      "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.6.8.tgz",
+      "integrity": "sha512-M0pkhzcgV31h++2901BiRXWl69hp2zPyLxRrSwRjd1ErXbNoubz/f4M6DrRTd4OiSUrT4ajRQzrmtS5plG4FtA==",
       "dev": true,
-      "license": "BSD-3-Clause",
       "dependencies": {
-        "hoist-non-react-statics": "^3.3.0",
-        "intl-format-cache": "^2.0.5",
-        "intl-messageformat": "^2.1.0",
-        "intl-relativeformat": "^2.1.0",
-        "invariant": "^2.1.1"
+        "@formatjs/ecma402-abstract": "2.0.0",
+        "@formatjs/icu-messageformat-parser": "2.7.8",
+        "@formatjs/intl": "2.10.4",
+        "@formatjs/intl-displaynames": "6.6.8",
+        "@formatjs/intl-listformat": "7.5.7",
+        "@types/hoist-non-react-statics": "^3.3.1",
+        "@types/react": "16 || 17 || 18",
+        "hoist-non-react-statics": "^3.3.2",
+        "intl-messageformat": "10.5.14",
+        "tslib": "^2.4.0"
       },
       "peerDependencies": {
-        "prop-types": "^15.5.4",
-        "react": "^0.14.9 || ^15.0.0 || ^16.0.0"
+        "react": "^16.6.0 || 17 || 18",
+        "typescript": "^4.7 || 5"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
       }
     },
     "node_modules/react-intl-redux": {
@@ -26430,7 +26541,6 @@
       "resolved": "https://registry.npmjs.org/react-intl-redux/-/react-intl-redux-2.4.1.tgz",
       "integrity": "sha512-EYTNmHJTnTam4phQj1nTdJvcdVjz+F56nLl6JtpqWsKzG5ZnQh/hoqLLJUjP0dgeNKSESIcjhYsTyBWDUwjo0A==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "@babel/runtime": "^7.17.9",
         "prop-types": "^15.8.1"
@@ -31912,6 +32022,12 @@
         "node": ">=4"
       }
     },
+    "node_modules/tslib": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
+      "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
+      "dev": true
+    },
     "node_modules/tty-browserify": {
       "version": "0.0.0",
       "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
diff --git a/package.json b/package.json
index 34ab4872..f739018f 100644
--- a/package.json
+++ b/package.json
@@ -35,7 +35,7 @@
   "peerDependencies": {
     "react": "^16",
     "react-dom": "^16",
-    "react-intl": "^2",
+    "react-intl": "^6",
     "react-intl-redux": "^0.7 || ^2.0.0",
     "react-popover": "^0.5",
     "react-redux": "^5",
@@ -81,7 +81,7 @@
     "raf": "3.4.1",
     "react": "16.14.0",
     "react-dom": "16.14.0",
-    "react-intl": "2.9.0",
+    "react-intl": "^6.6.8",
     "react-intl-redux": "2.4.1",
     "react-popover": "0.5.10",
     "react-redux": "5.1.2",
@@ -121,7 +121,10 @@
     "moduleNameMapper": {
       "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/test/__mocks__/fileMock.js",
       "\\.(css|less)$": "<rootDir>/test/__mocks__/styleMock.js"
-    }
+    },
+    "transformIgnorePatterns": [
+      "/node_modules/(?!intl-messageformat|intl-messageformat-parser).+\\.js$"
+    ]
   },
   "jest-junit": {
     "outputDirectory": "./test/results"
diff --git a/src/components/color-picker/color-picker.jsx b/src/components/color-picker/color-picker.jsx
index 398c5cc6..d09c90e1 100644
--- a/src/components/color-picker/color-picker.jsx
+++ b/src/components/color-picker/color-picker.jsx
@@ -1,6 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import {defineMessages, FormattedMessage, injectIntl, intlShape} from 'react-intl';
+import {defineMessages, FormattedMessage, injectIntl} from 'react-intl';
+import intlShape from '../../lib/intl-shape.js';
 
 import classNames from 'classnames';
 import parseColor from 'parse-color';
diff --git a/src/components/coming-soon/coming-soon.jsx b/src/components/coming-soon/coming-soon.jsx
index fa9285b0..5644d926 100644
--- a/src/components/coming-soon/coming-soon.jsx
+++ b/src/components/coming-soon/coming-soon.jsx
@@ -4,7 +4,8 @@ See #13 */
 
 import bindAll from 'lodash.bindall';
 import classNames from 'classnames';
-import {defineMessages, injectIntl, intlShape, FormattedMessage} from 'react-intl';
+import {defineMessages, injectIntl, FormattedMessage} from 'react-intl';
+import intlShape from '../../lib/intl-shape.js';
 import PropTypes from 'prop-types';
 import React from 'react';
 import ReactTooltip from 'react-tooltip';
diff --git a/src/components/fixed-tools/fixed-tools.jsx b/src/components/fixed-tools/fixed-tools.jsx
index b1d325fa..bd675fcb 100644
--- a/src/components/fixed-tools/fixed-tools.jsx
+++ b/src/components/fixed-tools/fixed-tools.jsx
@@ -11,7 +11,7 @@ import BufferedInputHOC from '../forms/buffered-input-hoc.jsx';
 import Button from '../button/button.jsx';
 import ButtonGroup from '../button-group/button-group.jsx';
 import Dropdown from '../dropdown/dropdown.jsx';
-import {defineMessages, injectIntl, intlShape} from 'react-intl';
+import {defineMessages, useIntl} from 'react-intl';
 import Formats, {isVector} from '../../lib/format';
 import Input from '../forms/input.jsx';
 import InputGroup from '../input-group/input-group.jsx';
@@ -87,13 +87,14 @@ const messages = defineMessages({
 const FixedToolsComponent = props => {
     const redoDisabled = !props.canRedo();
     const undoDisabled = !props.canUndo();
+    const intl = useIntl();
 
     return (
         <div className={styles.row}>
             {/* Name field */}
             <InputGroup>
                 <MediaQuery minWidth={layout.fullSizeEditorMinWidth}>
-                    <Label text={props.intl.formatMessage(messages.costume)}>
+                    <Label text={intl.formatMessage(messages.costume)}>
                         <BufferedInput
                             className={styles.costumeInput}
                             type="text"
@@ -128,7 +129,7 @@ const FixedToolsComponent = props => {
                         onClick={props.onUndo}
                     >
                         <img
-                            alt={props.intl.formatMessage(messages.undo)}
+                            alt={intl.formatMessage(messages.undo)}
                             className={classNames(
                                 styles.buttonGroupButtonIcon,
                                 styles.undoIcon
@@ -150,7 +151,7 @@ const FixedToolsComponent = props => {
                         onClick={props.onRedo}
                     >
                         <img
-                            alt={props.intl.formatMessage(messages.redo)}
+                            alt={intl.formatMessage(messages.redo)}
                             className={styles.buttonGroupButtonIcon}
                             draggable={false}
                             src={redoIcon}
@@ -164,16 +165,16 @@ const FixedToolsComponent = props => {
                 <InputGroup className={styles.modDashedBorder}>
                     <LabeledIconButton
                         disabled={!shouldShowGroup()}
-                        hideLabel={hideLabel(props.intl.locale)}
+                        hideLabel={hideLabel(intl.locale)}
                         imgSrc={groupIcon}
-                        title={props.intl.formatMessage(messages.group)}
+                        title={intl.formatMessage(messages.group)}
                         onClick={props.onGroup}
                     />
                     <LabeledIconButton
                         disabled={!shouldShowUngroup()}
-                        hideLabel={hideLabel(props.intl.locale)}
+                        hideLabel={hideLabel(intl.locale)}
                         imgSrc={ungroupIcon}
-                        title={props.intl.formatMessage(messages.ungroup)}
+                        title={intl.formatMessage(messages.ungroup)}
                         onClick={props.onUngroup}
                     />
                 </InputGroup> : null
@@ -184,16 +185,16 @@ const FixedToolsComponent = props => {
                 <InputGroup className={styles.modDashedBorder}>
                     <LabeledIconButton
                         disabled={!shouldShowBringForward()}
-                        hideLabel={hideLabel(props.intl.locale)}
+                        hideLabel={hideLabel(intl.locale)}
                         imgSrc={sendForwardIcon}
-                        title={props.intl.formatMessage(messages.forward)}
+                        title={intl.formatMessage(messages.forward)}
                         onClick={props.onSendForward}
                     />
                     <LabeledIconButton
                         disabled={!shouldShowSendBackward()}
-                        hideLabel={hideLabel(props.intl.locale)}
+                        hideLabel={hideLabel(intl.locale)}
                         imgSrc={sendBackwardIcon}
-                        title={props.intl.formatMessage(messages.backward)}
+                        title={intl.formatMessage(messages.backward)}
                         onClick={props.onSendBackward}
                     />
                 </InputGroup> : null
@@ -204,16 +205,16 @@ const FixedToolsComponent = props => {
                     <InputGroup className={styles.row}>
                         <LabeledIconButton
                             disabled={!shouldShowBringForward()}
-                            hideLabel={hideLabel(props.intl.locale)}
+                            hideLabel={hideLabel(intl.locale)}
                             imgSrc={sendFrontIcon}
-                            title={props.intl.formatMessage(messages.front)}
+                            title={intl.formatMessage(messages.front)}
                             onClick={props.onSendToFront}
                         />
                         <LabeledIconButton
                             disabled={!shouldShowSendBackward()}
-                            hideLabel={hideLabel(props.intl.locale)}
+                            hideLabel={hideLabel(intl.locale)}
                             imgSrc={sendBackIcon}
-                            title={props.intl.formatMessage(messages.back)}
+                            title={intl.formatMessage(messages.back)}
                             onClick={props.onSendToBack}
                         />
                     </InputGroup>
@@ -252,7 +253,7 @@ const FixedToolsComponent = props => {
                                             draggable={false}
                                             src={sendFrontIcon}
                                         />
-                                        <span>{props.intl.formatMessage(messages.front)}</span>
+                                        <span>{intl.formatMessage(messages.front)}</span>
                                     </Button>
                                     <Button
                                         className={classNames(styles.modMenuItem, {
@@ -266,7 +267,7 @@ const FixedToolsComponent = props => {
                                             draggable={false}
                                             src={sendBackIcon}
                                         />
-                                        <span>{props.intl.formatMessage(messages.back)}</span>
+                                        <span>{intl.formatMessage(messages.back)}</span>
                                     </Button>
 
                                     {/* To be rotation point */}
@@ -285,7 +286,7 @@ const FixedToolsComponent = props => {
                             }
                             tipSize={.01}
                         >
-                            {props.intl.formatMessage(messages.more)}
+                            {intl.formatMessage(messages.more)}
                         </Dropdown>
                     </InputGroup>
                 </MediaQuery> : null
@@ -298,7 +299,6 @@ FixedToolsComponent.propTypes = {
     canRedo: PropTypes.func.isRequired,
     canUndo: PropTypes.func.isRequired,
     format: PropTypes.oneOf(Object.keys(Formats)),
-    intl: intlShape,
     name: PropTypes.string,
     onGroup: PropTypes.func.isRequired,
     onRedo: PropTypes.func.isRequired,
@@ -321,4 +321,4 @@ const mapStateToProps = state => ({
 
 export default connect(
     mapStateToProps
-)(injectIntl(FixedToolsComponent));
+)(FixedToolsComponent);
diff --git a/src/components/mode-tools/mode-tools.jsx b/src/components/mode-tools/mode-tools.jsx
index 78771edb..a86f4ea4 100644
--- a/src/components/mode-tools/mode-tools.jsx
+++ b/src/components/mode-tools/mode-tools.jsx
@@ -12,7 +12,7 @@ import {setShapesFilled} from '../../reducers/fill-bitmap-shapes';
 import FontDropdown from '../../containers/font-dropdown.jsx';
 import LiveInputHOC from '../forms/live-input-hoc.jsx';
 import Label from '../forms/label.jsx';
-import {defineMessages, injectIntl, intlShape} from 'react-intl';
+import {defineMessages, useIntl} from 'react-intl';
 import Input from '../forms/input.jsx';
 import InputGroup from '../input-group/input-group.jsx';
 import LabeledIconButton from '../labeled-icon-button/labeled-icon-button.jsx';
@@ -43,6 +43,7 @@ import {MAX_STROKE_WIDTH} from '../../reducers/stroke-width';
 
 const LiveInput = LiveInputHOC(Input);
 const ModeToolsComponent = props => {
+    const intl = useIntl();
     const messages = defineMessages({
         brushSize: {
             defaultMessage: 'Size',
@@ -122,7 +123,7 @@ const ModeToolsComponent = props => {
             <div className={classNames(props.className, styles.modeTools)}>
                 <div>
                     <img
-                        alt={props.intl.formatMessage(currentMessage)}
+                        alt={intl.formatMessage(currentMessage)}
                         className={styles.modeToolsIcon}
                         draggable={false}
                         src={currentIcon}
@@ -151,7 +152,7 @@ const ModeToolsComponent = props => {
             <div className={classNames(props.className, styles.modeTools)}>
                 <div>
                     <img
-                        alt={props.intl.formatMessage(messages.eraserSize)}
+                        alt={intl.formatMessage(messages.eraserSize)}
                         className={styles.modeToolsIcon}
                         draggable={false}
                         src={currentIcon}
@@ -175,24 +176,24 @@ const ModeToolsComponent = props => {
                 <InputGroup className={classNames(styles.modDashedBorder, styles.modLabeledIconHeight)}>
                     <LabeledIconButton
                         disabled={!props.hasSelectedUncurvedPoints}
-                        hideLabel={hideLabel(props.intl.locale)}
+                        hideLabel={hideLabel(intl.locale)}
                         imgSrc={curvedPointIcon}
-                        title={props.intl.formatMessage(messages.curved)}
+                        title={intl.formatMessage(messages.curved)}
                         onClick={props.onCurvePoints}
                     />
                     <LabeledIconButton
                         disabled={!props.hasSelectedUnpointedPoints}
-                        hideLabel={hideLabel(props.intl.locale)}
+                        hideLabel={hideLabel(intl.locale)}
                         imgSrc={straightPointIcon}
-                        title={props.intl.formatMessage(messages.pointed)}
+                        title={intl.formatMessage(messages.pointed)}
                         onClick={props.onPointPoints}
                     />
                 </InputGroup>
                 <InputGroup className={classNames(styles.modLabeledIconHeight)}>
                     <LabeledIconButton
-                        hideLabel={hideLabel(props.intl.locale)}
+                        hideLabel={hideLabel(intl.locale)}
                         imgSrc={deleteIcon}
-                        title={props.intl.formatMessage(messages.delete)}
+                        title={intl.formatMessage(messages.delete)}
                         onClick={props.onDelete}
                     />
                 </InputGroup>
@@ -205,38 +206,38 @@ const ModeToolsComponent = props => {
             <div className={classNames(props.className, styles.modeTools)}>
                 <InputGroup className={classNames(styles.modDashedBorder, styles.modLabeledIconHeight)}>
                     <LabeledIconButton
-                        hideLabel={hideLabel(props.intl.locale)}
+                        hideLabel={hideLabel(intl.locale)}
                         imgSrc={copyIcon}
-                        title={props.intl.formatMessage(messages.copy)}
+                        title={intl.formatMessage(messages.copy)}
                         onClick={props.onCopyToClipboard}
                     />
                     <LabeledIconButton
                         disabled={!(props.clipboardItems.length > 0)}
-                        hideLabel={hideLabel(props.intl.locale)}
+                        hideLabel={hideLabel(intl.locale)}
                         imgSrc={pasteIcon}
-                        title={props.intl.formatMessage(messages.paste)}
+                        title={intl.formatMessage(messages.paste)}
                         onClick={props.onPasteFromClipboard}
                     />
                 </InputGroup>
                 <InputGroup className={classNames(styles.modDashedBorder, styles.modLabeledIconHeight)}>
                     <LabeledIconButton
-                        hideLabel={hideLabel(props.intl.locale)}
+                        hideLabel={hideLabel(intl.locale)}
                         imgSrc={deleteIcon}
-                        title={props.intl.formatMessage(messages.delete)}
+                        title={intl.formatMessage(messages.delete)}
                         onClick={props.onDelete}
                     />
                 </InputGroup>
                 <InputGroup className={classNames(styles.modLabeledIconHeight)}>
                     <LabeledIconButton
-                        hideLabel={props.intl.locale !== 'en'}
+                        hideLabel={intl.locale !== 'en'}
                         imgSrc={flipHorizontalIcon}
-                        title={props.intl.formatMessage(messages.flipHorizontal)}
+                        title={intl.formatMessage(messages.flipHorizontal)}
                         onClick={props.onFlipHorizontal}
                     />
                     <LabeledIconButton
-                        hideLabel={props.intl.locale !== 'en'}
+                        hideLabel={intl.locale !== 'en'}
                         imgSrc={flipVerticalIcon}
-                        title={props.intl.formatMessage(messages.flipVertical)}
+                        title={intl.formatMessage(messages.flipVertical)}
                         onClick={props.onFlipVertical}
                     />
                 </InputGroup>
@@ -266,7 +267,7 @@ const ModeToolsComponent = props => {
                     <LabeledIconButton
                         highlighted={props.fillBitmapShapes}
                         imgSrc={fillIcon}
-                        title={props.intl.formatMessage(messages.filled)}
+                        title={intl.formatMessage(messages.filled)}
                         onClick={props.onFillShapes}
                     />
                 </InputGroup>
@@ -274,13 +275,13 @@ const ModeToolsComponent = props => {
                     <LabeledIconButton
                         highlighted={!props.fillBitmapShapes}
                         imgSrc={outlineIcon}
-                        title={props.intl.formatMessage(messages.outlined)}
+                        title={intl.formatMessage(messages.outlined)}
                         onClick={props.onOutlineShapes}
                     />
                 </InputGroup>
                 {props.fillBitmapShapes ? null : (
                     <InputGroup>
-                        <Label text={props.intl.formatMessage(messages.thickness)}>
+                        <Label text={intl.formatMessage(messages.thickness)}>
                             <LiveInput
                                 range
                                 small
@@ -315,7 +316,6 @@ ModeToolsComponent.propTypes = {
     format: PropTypes.oneOf(Object.keys(Formats)),
     hasSelectedUncurvedPoints: PropTypes.bool,
     hasSelectedUnpointedPoints: PropTypes.bool,
-    intl: intlShape.isRequired,
     mode: PropTypes.string.isRequired,
     onBitBrushSliderChange: PropTypes.func.isRequired,
     onBitEraserSliderChange: PropTypes.func.isRequired,
@@ -367,4 +367,4 @@ const mapDispatchToProps = dispatch => ({
 export default connect(
     mapStateToProps,
     mapDispatchToProps
-)(injectIntl(ModeToolsComponent));
+)(ModeToolsComponent);
diff --git a/src/components/paint-editor/paint-editor.jsx b/src/components/paint-editor/paint-editor.jsx
index c94d3585..7ac4e062 100644
--- a/src/components/paint-editor/paint-editor.jsx
+++ b/src/components/paint-editor/paint-editor.jsx
@@ -1,6 +1,6 @@
 import paper from '@scratch/paper';
 import classNames from 'classnames';
-import {defineMessages, injectIntl, intlShape} from 'react-intl';
+import {defineMessages, useIntl} from 'react-intl';
 import React from 'react';
 import PropTypes from 'prop-types';
 
@@ -55,56 +55,29 @@ const messages = defineMessages({
     }
 });
 
-const PaintEditorComponent = props => (
-    <div
-        className={styles.editorContainer}
-        dir={props.rtl ? 'rtl' : 'ltr'}
-    >
-        {props.canvas !== null ? ( // eslint-disable-line no-negated-condition
-            <div className={styles.editorContainerTop}>
-                {/* First row */}
-                <div className={styles.row}>
-                    <FixedToolsContainer
-                        canRedo={props.canRedo}
-                        canUndo={props.canUndo}
-                        name={props.name}
-                        onRedo={props.onRedo}
-                        onUndo={props.onUndo}
-                        onUpdateImage={props.onUpdateImage}
-                        onUpdateName={props.onUpdateName}
-                    />
-                </div>
-                {/* Second Row */}
-                {isVector(props.format) ?
+const PaintEditorComponent = props => {
+    const intl = useIntl();
+    return (
+        <div
+            className={styles.editorContainer}
+            dir={props.rtl ? 'rtl' : 'ltr'}
+        >
+            {props.canvas !== null ? ( // eslint-disable-line no-negated-condition
+                <div className={styles.editorContainerTop}>
+                    {/* First row */}
                     <div className={styles.row}>
-                        <InputGroup
-                            className={classNames(
-                                styles.row,
-                                styles.modDashedBorder,
-                                styles.modLabeledIconHeight
-                            )}
-                        >
-                            {/* fill */}
-                            <FillColorIndicatorComponent
-                                className={styles.modMarginAfter}
-                                onUpdateImage={props.onUpdateImage}
-                            />
-                            {/* stroke */}
-                            <StrokeColorIndicatorComponent
-                                onUpdateImage={props.onUpdateImage}
-                            />
-                            {/* stroke width */}
-                            <StrokeWidthIndicatorComponent
-                                onUpdateImage={props.onUpdateImage}
-                            />
-                        </InputGroup>
-                        <InputGroup className={styles.modModeTools}>
-                            <ModeToolsContainer
-                                onUpdateImage={props.onUpdateImage}
-                            />
-                        </InputGroup>
-                    </div> :
-                    isBitmap(props.format) ?
+                        <FixedToolsContainer
+                            canRedo={props.canRedo}
+                            canUndo={props.canUndo}
+                            name={props.name}
+                            onRedo={props.onRedo}
+                            onUndo={props.onUndo}
+                            onUpdateImage={props.onUpdateImage}
+                            onUpdateName={props.onUpdateName}
+                        />
+                    </div>
+                    {/* Second Row */}
+                    {isVector(props.format) ?
                         <div className={styles.row}>
                             <InputGroup
                                 className={classNames(
@@ -118,136 +91,151 @@ const PaintEditorComponent = props => (
                                     className={styles.modMarginAfter}
                                     onUpdateImage={props.onUpdateImage}
                                 />
+                                {/* stroke */}
+                                <StrokeColorIndicatorComponent
+                                    onUpdateImage={props.onUpdateImage}
+                                />
+                                {/* stroke width */}
+                                <StrokeWidthIndicatorComponent
+                                    onUpdateImage={props.onUpdateImage}
+                                />
                             </InputGroup>
                             <InputGroup className={styles.modModeTools}>
                                 <ModeToolsContainer
                                     onUpdateImage={props.onUpdateImage}
                                 />
                             </InputGroup>
-                        </div> : null
-                }
-            </div>
-        ) : null}
-
-        <div className={styles.topAlignRow}>
-            {/* Modes */}
-            {props.canvas !== null && isVector(props.format) ? ( // eslint-disable-line no-negated-condition
-                <div className={styles.modeSelector}>
-                    <SelectMode
-                        onUpdateImage={props.onUpdateImage}
-                    />
-                    <ReshapeMode
-                        onUpdateImage={props.onUpdateImage}
-                    />
-                    <BrushMode
-                        onUpdateImage={props.onUpdateImage}
-                    />
-                    <EraserMode
-                        onUpdateImage={props.onUpdateImage}
-                    />
-                    <FillMode
-                        onUpdateImage={props.onUpdateImage}
-                    />
-                    <TextMode
-                        textArea={props.textArea}
-                        onUpdateImage={props.onUpdateImage}
-                    />
-                    <LineMode
-                        onUpdateImage={props.onUpdateImage}
-                    />
-                    <OvalMode
-                        onUpdateImage={props.onUpdateImage}
-                    />
-                    <RectMode
-                        onUpdateImage={props.onUpdateImage}
-                    />
-                </div>
-            ) : null}
-
-            {props.canvas !== null && isBitmap(props.format) ? ( // eslint-disable-line no-negated-condition
-                <div className={styles.modeSelector}>
-                    <BitBrushMode
-                        onUpdateImage={props.onUpdateImage}
-                    />
-                    <BitLineMode
-                        onUpdateImage={props.onUpdateImage}
-                    />
-                    <BitOvalMode
-                        onUpdateImage={props.onUpdateImage}
-                    />
-                    <BitRectMode
-                        onUpdateImage={props.onUpdateImage}
-                    />
-                    <TextMode
-                        isBitmap
-                        textArea={props.textArea}
-                        onUpdateImage={props.onUpdateImage}
-                    />
-                    <BitFillMode
-                        onUpdateImage={props.onUpdateImage}
-                    />
-                    <BitEraserMode
-                        onUpdateImage={props.onUpdateImage}
-                    />
-                    <BitSelectMode
-                        onUpdateImage={props.onUpdateImage}
-                    />
-                </div>
-            ) : null}
-
-            <div className={styles.controlsContainer}>
-                {/* Canvas */}
-                <ScrollableCanvas
-                    canvas={props.canvas}
-                    hideScrollbars={props.isEyeDropping}
-                    style={styles.canvasContainer}
-                >
-                    <PaperCanvas
-                        canvasRef={props.setCanvas}
-                        image={props.image}
-                        imageFormat={props.imageFormat}
-                        imageId={props.imageId}
-                        rotationCenterX={props.rotationCenterX}
-                        rotationCenterY={props.rotationCenterY}
-                        zoomLevelId={props.zoomLevelId}
-                        onUpdateImage={props.onUpdateImage}
-                    />
-                    <textarea
-                        className={styles.textArea}
-                        ref={props.setTextArea}
-                        spellCheck={false}
-                    />
-                    {props.isEyeDropping &&
-                        props.colorInfo !== null &&
-                        !props.colorInfo.hideLoupe ? (
-                            <Box className={styles.colorPickerWrapper}>
-                                <Loupe
-                                    colorInfo={props.colorInfo}
-                                    pixelRatio={paper.project.view.pixelRatio}
-                                />
-                            </Box>
-                        ) : null
-                    }
-                </ScrollableCanvas>
-                <div className={styles.canvasControls}>
-                    {isVector(props.format) ?
-                        <Button
-                            className={styles.bitmapButton}
-                            onClick={props.onSwitchToBitmap}
-                        >
-                            <img
-                                className={styles.bitmapButtonIcon}
-                                draggable={false}
-                                src={bitmapIcon}
-                            />
-                            <span className={styles.buttonText}>
-                                {props.intl.formatMessage(messages.bitmap)}
-                            </span>
-                        </Button> :
+                        </div> :
                         isBitmap(props.format) ?
+                            <div className={styles.row}>
+                                <InputGroup
+                                    className={classNames(
+                                        styles.row,
+                                        styles.modDashedBorder,
+                                        styles.modLabeledIconHeight
+                                    )}
+                                >
+                                    {/* fill */}
+                                    <FillColorIndicatorComponent
+                                        className={styles.modMarginAfter}
+                                        onUpdateImage={props.onUpdateImage}
+                                    />
+                                </InputGroup>
+                                <InputGroup className={styles.modModeTools}>
+                                    <ModeToolsContainer
+                                        onUpdateImage={props.onUpdateImage}
+                                    />
+                                </InputGroup>
+                            </div> : null
+                    }
+                </div>
+            ) : null}
+
+            <div className={styles.topAlignRow}>
+                {/* Modes */}
+                {props.canvas !== null && isVector(props.format) ? ( // eslint-disable-line no-negated-condition
+                    <div className={styles.modeSelector}>
+                        <SelectMode
+                            onUpdateImage={props.onUpdateImage}
+                        />
+                        <ReshapeMode
+                            onUpdateImage={props.onUpdateImage}
+                        />
+                        <BrushMode
+                            onUpdateImage={props.onUpdateImage}
+                        />
+                        <EraserMode
+                            onUpdateImage={props.onUpdateImage}
+                        />
+                        <FillMode
+                            onUpdateImage={props.onUpdateImage}
+                        />
+                        <TextMode
+                            textArea={props.textArea}
+                            onUpdateImage={props.onUpdateImage}
+                        />
+                        <LineMode
+                            onUpdateImage={props.onUpdateImage}
+                        />
+                        <OvalMode
+                            onUpdateImage={props.onUpdateImage}
+                        />
+                        <RectMode
+                            onUpdateImage={props.onUpdateImage}
+                        />
+                    </div>
+                ) : null}
+
+                {props.canvas !== null && isBitmap(props.format) ? ( // eslint-disable-line no-negated-condition
+                    <div className={styles.modeSelector}>
+                        <BitBrushMode
+                            onUpdateImage={props.onUpdateImage}
+                        />
+                        <BitLineMode
+                            onUpdateImage={props.onUpdateImage}
+                        />
+                        <BitOvalMode
+                            onUpdateImage={props.onUpdateImage}
+                        />
+                        <BitRectMode
+                            onUpdateImage={props.onUpdateImage}
+                        />
+                        <TextMode
+                            isBitmap
+                            textArea={props.textArea}
+                            onUpdateImage={props.onUpdateImage}
+                        />
+                        <BitFillMode
+                            onUpdateImage={props.onUpdateImage}
+                        />
+                        <BitEraserMode
+                            onUpdateImage={props.onUpdateImage}
+                        />
+                        <BitSelectMode
+                            onUpdateImage={props.onUpdateImage}
+                        />
+                    </div>
+                ) : null}
+
+                <div className={styles.controlsContainer}>
+                    {/* Canvas */}
+                    <ScrollableCanvas
+                        canvas={props.canvas}
+                        hideScrollbars={props.isEyeDropping}
+                        style={styles.canvasContainer}
+                    >
+                        <PaperCanvas
+                            canvasRef={props.setCanvas}
+                            image={props.image}
+                            imageFormat={props.imageFormat}
+                            imageId={props.imageId}
+                            rotationCenterX={props.rotationCenterX}
+                            rotationCenterY={props.rotationCenterY}
+                            zoomLevelId={props.zoomLevelId}
+                            onUpdateImage={props.onUpdateImage}
+                        />
+                        <textarea
+                            className={styles.textArea}
+                            ref={props.setTextArea}
+                            spellCheck={false}
+                        />
+                        {props.isEyeDropping &&
+                            props.colorInfo !== null &&
+                            !props.colorInfo.hideLoupe ? (
+                                <Box className={styles.colorPickerWrapper}>
+                                    <Loupe
+                                        colorInfo={props.colorInfo}
+                                        pixelRatio={paper.project.view.pixelRatio}
+                                    />
+                                </Box>
+                            ) : null
+                        }
+                    </ScrollableCanvas>
+                    <div className={styles.canvasControls}>
+                        {isVector(props.format) ?
                             <Button
                                 className={styles.bitmapButton}
-                                onClick={props.onSwitchToVector}
+                                onClick={props.onSwitchToBitmap}
                             >
                                 <img
                                     className={styles.bitmapButtonIcon}
@@ -255,53 +243,68 @@ const PaintEditorComponent = props => (
                                     src={bitmapIcon}
                                 />
                                 <span className={styles.buttonText}>
-                                    {props.intl.formatMessage(messages.vector)}
+                                    {intl.formatMessage(messages.bitmap)}
                                 </span>
-                            </Button> : null
-                    }
-                    {/* Zoom controls */}
-                    <InputGroup className={styles.zoomControls}>
-                        <ButtonGroup>
-                            <Button
-                                className={styles.buttonGroupButton}
-                                onClick={props.onZoomOut}
-                            >
-                                <img
-                                    alt="Zoom Out"
-                                    className={styles.buttonGroupButtonIcon}
-                                    draggable={false}
-                                    src={zoomOutIcon}
-                                />
-                            </Button>
-                            <Button
-                                className={styles.buttonGroupButton}
-                                onClick={props.onZoomReset}
-                            >
-                                <img
-                                    alt="Zoom Reset"
-                                    className={styles.buttonGroupButtonIcon}
-                                    draggable={false}
-                                    src={zoomResetIcon}
-                                />
-                            </Button>
-                            <Button
-                                className={styles.buttonGroupButton}
-                                onClick={props.onZoomIn}
-                            >
-                                <img
-                                    alt="Zoom In"
-                                    className={styles.buttonGroupButtonIcon}
-                                    draggable={false}
-                                    src={zoomInIcon}
-                                />
-                            </Button>
-                        </ButtonGroup>
-                    </InputGroup>
+                            </Button> :
+                            isBitmap(props.format) ?
+                                <Button
+                                    className={styles.bitmapButton}
+                                    onClick={props.onSwitchToVector}
+                                >
+                                    <img
+                                        className={styles.bitmapButtonIcon}
+                                        draggable={false}
+                                        src={bitmapIcon}
+                                    />
+                                    <span className={styles.buttonText}>
+                                        {intl.formatMessage(messages.vector)}
+                                    </span>
+                                </Button> : null
+                        }
+                        {/* Zoom controls */}
+                        <InputGroup className={styles.zoomControls}>
+                            <ButtonGroup>
+                                <Button
+                                    className={styles.buttonGroupButton}
+                                    onClick={props.onZoomOut}
+                                >
+                                    <img
+                                        alt="Zoom Out"
+                                        className={styles.buttonGroupButtonIcon}
+                                        draggable={false}
+                                        src={zoomOutIcon}
+                                    />
+                                </Button>
+                                <Button
+                                    className={styles.buttonGroupButton}
+                                    onClick={props.onZoomReset}
+                                >
+                                    <img
+                                        alt="Zoom Reset"
+                                        className={styles.buttonGroupButtonIcon}
+                                        draggable={false}
+                                        src={zoomResetIcon}
+                                    />
+                                </Button>
+                                <Button
+                                    className={styles.buttonGroupButton}
+                                    onClick={props.onZoomIn}
+                                >
+                                    <img
+                                        alt="Zoom In"
+                                        className={styles.buttonGroupButtonIcon}
+                                        draggable={false}
+                                        src={zoomInIcon}
+                                    />
+                                </Button>
+                            </ButtonGroup>
+                        </InputGroup>
+                    </div>
                 </div>
             </div>
         </div>
-    </div>
-);
+    );
+};
 
 PaintEditorComponent.propTypes = {
     canRedo: PropTypes.func.isRequired,
@@ -315,7 +318,6 @@ PaintEditorComponent.propTypes = {
     ]),
     imageFormat: PropTypes.string,
     imageId: PropTypes.string,
-    intl: intlShape,
     isEyeDropping: PropTypes.bool,
     name: PropTypes.string,
     onRedo: PropTypes.func.isRequired,
@@ -336,4 +338,4 @@ PaintEditorComponent.propTypes = {
     zoomLevelId: PropTypes.string
 };
 
-export default injectIntl(PaintEditorComponent);
+export default PaintEditorComponent;
diff --git a/src/components/tool-select-base/tool-select-base.jsx b/src/components/tool-select-base/tool-select-base.jsx
index d9b2be00..8c866f64 100644
--- a/src/components/tool-select-base/tool-select-base.jsx
+++ b/src/components/tool-select-base/tool-select-base.jsx
@@ -1,31 +1,34 @@
 import classNames from 'classnames';
 import React from 'react';
 import PropTypes from 'prop-types';
-import {injectIntl, intlShape} from 'react-intl';
+import {useIntl} from 'react-intl';
 
 import Button from '../button/button.jsx';
 
 import styles from './tool-select-base.css';
 
-const ToolSelectComponent = props => (
-    <Button
-        className={
-            classNames(props.className, styles.modToolSelect, {
-                [styles.isSelected]: props.isSelected
-            })
-        }
-        disabled={props.disabled}
-        title={props.intl.formatMessage(props.imgDescriptor)}
-        onClick={props.onMouseDown}
-    >
-        <img
-            alt={props.intl.formatMessage(props.imgDescriptor)}
-            className={styles.toolSelectIcon}
-            draggable={false}
-            src={props.imgSrc}
-        />
-    </Button>
-);
+const ToolSelectComponent = props => {
+    const intl = useIntl();
+    return (
+        <Button
+            className={
+                classNames(props.className, styles.modToolSelect, {
+                    [styles.isSelected]: props.isSelected
+                })
+            }
+            disabled={props.disabled}
+            title={intl.formatMessage(props.imgDescriptor)}
+            onClick={props.onMouseDown}
+        >
+            <img
+                alt={intl.formatMessage(props.imgDescriptor)}
+                className={styles.toolSelectIcon}
+                draggable={false}
+                src={props.imgSrc}
+            />
+        </Button>
+    );
+};
 
 ToolSelectComponent.propTypes = {
     className: PropTypes.string,
@@ -36,9 +39,8 @@ ToolSelectComponent.propTypes = {
         id: PropTypes.string
     }).isRequired,
     imgSrc: PropTypes.string.isRequired,
-    intl: intlShape.isRequired,
     isSelected: PropTypes.bool.isRequired,
     onMouseDown: PropTypes.func.isRequired
 };
 
-export default injectIntl(ToolSelectComponent);
+export default ToolSelectComponent;
diff --git a/src/containers/color-indicator.jsx b/src/containers/color-indicator.jsx
index 10b5faf0..e6c9869b 100644
--- a/src/containers/color-indicator.jsx
+++ b/src/containers/color-indicator.jsx
@@ -2,7 +2,8 @@ import PropTypes from 'prop-types';
 import React from 'react';
 import bindAll from 'lodash.bindall';
 import parseColor from 'parse-color';
-import {injectIntl, intlShape} from 'react-intl';
+import {injectIntl} from 'react-intl';
+import intlShape from '../lib/intl-shape.js';
 
 import {getSelectedLeafItems} from '../helper/selection';
 import Formats, {isBitmap} from '../lib/format';
diff --git a/src/lib/intl-shape.js b/src/lib/intl-shape.js
new file mode 100644
index 00000000..d970032c
--- /dev/null
+++ b/src/lib/intl-shape.js
@@ -0,0 +1,10 @@
+import PropTypes from 'prop-types';
+
+// intlShape was removed in react-intl@3 and replaced with a TypeScript interface.
+// These are some of the commonly used properties from the intl object.
+const intlShape = PropTypes.shape({
+    locale: PropTypes.string.isRequired,
+    formatMessage: PropTypes.func.isRequired
+});
+
+export default intlShape;
diff --git a/src/playground/reducers/intl.js b/src/playground/reducers/intl.js
index fc753d97..1a7463fe 100644
--- a/src/playground/reducers/intl.js
+++ b/src/playground/reducers/intl.js
@@ -1,15 +1,8 @@
-import {addLocaleData} from 'react-intl';
 import {updateIntl as superUpdateIntl} from 'react-intl-redux';
 import {IntlProvider, intlReducer} from 'react-intl-redux';
 
-import localeData from 'scratch-l10n';
 import paintMessages from 'scratch-l10n/locales/paint-editor-msgs';
 
-Object.keys(localeData).forEach(locale => {
-    // TODO: will need to handle locales not in the default intl - see www/custom-locales
-    addLocaleData(localeData[locale].localeData);
-});
-
 const intlInitialState = {
     intl: {
         defaultLocale: 'en',
diff --git a/test/__mocks__/react-intl.js b/test/__mocks__/react-intl.js
index 181caea9..e99bfdf9 100644
--- a/test/__mocks__/react-intl.js
+++ b/test/__mocks__/react-intl.js
@@ -11,7 +11,6 @@ const intl = {
     formatRelative: ({defaultMessage}) => defaultMessage,
     formatNumber: ({defaultMessage}) => defaultMessage,
     formatPlural: ({defaultMessage}) => defaultMessage,
-    formatHTMLMessage: ({defaultMessage}) => defaultMessage,
     now: () => 0
 };
 
diff --git a/webpack.config.js b/webpack.config.js
index 0b6b68fa..63e99e38 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -17,7 +17,12 @@ const base = {
         rules: [{
             test: /\.jsx?$/,
             loader: 'babel-loader',
-            include: path.resolve(__dirname, 'src'),
+            include: [
+                path.resolve(__dirname, 'src'),
+                path.join(__dirname, 'node_modules/react-intl'),
+                path.join(__dirname, 'node_modules/intl-messageformat'),
+                path.join(__dirname, 'node_modules/intl-messageformat-parser')
+            ],
             options: {
                 plugins: ['transform-object-rest-spread'],
                 presets: [