diff --git a/package.json b/package.json
index 8ca0423802..de1ece4e13 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
     "detect-indent": "^4.0.0",
     "diff": "^2.2.1",
     "ini": "^1.3.4",
+    "inquirer": "^1.2.2",
     "invariant": "^2.2.0",
     "is-builtin-module": "^1.0.0",
     "is-ci": "^1.0.9",
diff --git a/src/cli/aliases.js b/src/cli/aliases.js
index 41d72d735c..e650929284 100644
--- a/src/cli/aliases.js
+++ b/src/cli/aliases.js
@@ -28,7 +28,7 @@ export default ({
   rm: 'remove',
   show: 'info',
   uninstall: 'remove',
-  update: 'upgrade',
+  udpate: 'upgrade',
   verison: 'version',
   view: 'info',
 }: { [key: string]: ?string });
diff --git a/src/cli/commands/index.js b/src/cli/commands/index.js
index f8f4fbb747..bdb83ec696 100644
--- a/src/cli/commands/index.js
+++ b/src/cli/commands/index.js
@@ -31,6 +31,7 @@ import * as upgrade from './upgrade.js'; export {upgrade};
 import * as version from './version.js'; export {version};
 import * as versions from './versions.js'; export {versions};
 import * as why from './why.js'; export {why};
+import * as upgradeInteractive from './upgrade-interactive.js'; export {upgradeInteractive};
 
 import buildUseless from './_useless.js';
 
diff --git a/src/cli/commands/upgrade-interactive.js b/src/cli/commands/upgrade-interactive.js
new file mode 100644
index 0000000000..bc87067f2e
--- /dev/null
+++ b/src/cli/commands/upgrade-interactive.js
@@ -0,0 +1,156 @@
+/* @flow */
+
+import type {Reporter} from '../../reporters/index.js';
+import type Config from '../../config.js';
+import inquirer from 'inquirer';
+import repeat from 'repeating';
+import {MessageError} from '../../errors.js';
+import PackageRequest from '../../package-request.js';
+import {Add} from './add.js';
+import {Install} from './install.js';
+import Lockfile from '../../lockfile/wrapper.js';
+
+export const requireLockfile = true;
+
+export function setFlags(commander: Object) {
+  // TODO: support some flags that install command has
+  commander.usage('update');
+}
+
+type Dependency = {
+  name: string,
+  current: string,
+  wanted: string,
+  latest: string,
+  hint: ?string
+};
+
+type InquirerResponses<K, T> = {[key: K]: Array<T>};
+
+// Prompt user with Inquirer
+async function prompt(choices): Promise<Array<Dependency>> {
+  const answers: InquirerResponses<'packages', Dependency> = await inquirer.prompt([{
+    name: 'packages',
+    type: 'checkbox',
+    message: 'Choose which packages to update.',
+    choices,
+    // Couldn't make it work, I guess I'm missing something here
+    // $FlowFixMe: https://github.com/facebook/flow/blob/f41e66e27b227235750792c34f5a80f38bde6320/lib/node.js#L1197
+    pageSize: process.stdout.rows - 2,
+    validate: (answer) => !!answer.length || 'You must choose at least one package.',
+  }]);
+  return answers.packages;
+}
+
+export async function run(
+  config: Config,
+  reporter: Reporter,
+  flags: Object,
+  args: Array<string>,
+): Promise<void> {
+  const lockfile = await Lockfile.fromDirectory(config.cwd);
+  const install = new Install(flags, config, reporter, lockfile);
+  const [deps] = await install.fetchRequestFromCwd();
+
+  const allDeps = (await Promise.all(deps.map(async ({pattern, hint}): Promise<Dependency> => {
+    const locked = lockfile.getLocked(pattern);
+    if (!locked) {
+      throw new MessageError(reporter.lang('lockfileOutdated'));
+    }
+
+    const {name, version: current} = locked;
+    let latest = '';
+    let wanted = '';
+
+    const normalized = PackageRequest.normalizePattern(pattern);
+
+    if (PackageRequest.getExoticResolver(pattern) ||
+        PackageRequest.getExoticResolver(normalized.range)) {
+      latest = wanted = 'exotic';
+    } else {
+      ({latest, wanted} = await config.registries[locked.registry].checkOutdated(config, name, normalized.range));
+    }
+
+    return ({name, current, wanted, latest, hint});
+  })));
+
+  const isDepOld = ({latest, current}) => latest !== current;
+  const isDepExpected = ({current, wanted}) => current === wanted;
+
+  const outdatedDeps = allDeps
+    .filter(isDepOld)
+    .sort((depA, depB) => {
+      if (isDepExpected(depA) && !isDepExpected(depB)) {
+        return 1;
+      }
+      return -1;
+    });
+
+  const getNameFromHint = (hint) => hint ? `${hint}Dependencies` : 'dependencies';
+
+  const maxLengthArr = {name: 0, current: 0, latest: 0};
+  outdatedDeps.forEach((dep) =>
+    ['name', 'current', 'latest'].forEach((key) => {
+      maxLengthArr[key] = Math.max(maxLengthArr[key], dep[key].length);
+    }),
+  );
+
+  // Depends on maxLengthArr
+  const addPadding = (dep) => (key) =>
+    `${dep[key]}${repeat(' ', maxLengthArr[key] - dep[key].length)}`;
+
+  const colorizeName = ({current, wanted}) =>
+    (current === wanted) ? reporter.format.yellow : reporter.format.red;
+
+  const makeRow = (dep) => {
+    const padding = addPadding(dep);
+    const name = colorizeName(dep)(padding('name'));
+    const current = reporter.format.blue(padding('current'));
+    const latest = reporter.format.green(padding('latest'));
+    return `${name}  ${current}  ❯  ${latest}`;
+  };
+
+  const groupedDeps = outdatedDeps.reduce((acc, dep) => {
+    const {hint, name, latest} = dep;
+    const key = getNameFromHint(hint);
+    const xs = acc[key] || [];
+    acc[key] = xs.concat({
+      name: makeRow(dep),
+      value: dep,
+      short: `${name}@${latest}`,
+    });
+    return acc;
+  }, {});
+
+  const flatten = (xs) => xs.reduce(
+      (ys, y) => ys.concat(Array.isArray(y) ? flatten(y) : y), [],
+  );
+
+  const choices = Object.keys(groupedDeps).map((key) => [
+    new inquirer.Separator(reporter.format.bold.underline.green(key)),
+    groupedDeps[key],
+    new inquirer.Separator(' '),
+  ]);
+
+  const answers = await prompt(flatten(choices));
+
+  const getName = ({name}) => name;
+  const isHint = (x) => ({hint}) => hint === x;
+
+  await [null, 'dev', 'optional', 'peer'].reduce(async (promise, hint) => {
+    // Wait for previous promise to resolve
+    await promise;
+    // Reset dependency flags
+    flags.dev = hint === 'dev';
+    flags.peer = hint === 'peer';
+    flags.optional = hint === 'optional';
+
+    const deps = answers.filter(isHint(hint)).map(getName);
+    if (deps.length) {
+      reporter.info(reporter.lang('updateInstalling', getNameFromHint(hint)));
+      const add = new Add(deps, flags, config, reporter, lockfile);
+      return await add.init();
+    }
+    return Promise.resolve();
+  }, Promise.resolve());
+}
diff --git a/src/reporters/lang/en.js b/src/reporters/lang/en.js
index 0bca72f2f6..00321371c5 100644
--- a/src/reporters/lang/en.js
+++ b/src/reporters/lang/en.js
@@ -210,11 +210,11 @@ const messages = {
   cantRequestOffline: 'Can\'t make a request in offline mode',
   requestManagerNotSetupHAR: 'RequestManager was not setup to capture HAR files',
   requestError: 'Request $0 returned a $1',
-
   tarballNotInNetworkOrCache: '$0: Tarball is not in network and can not be located in cache ($1)',
   fetchBadHash: 'Bad hash. Expected $0 but got $1 ',
   fetchErrorCorrupt: '$0. Mirror tarball appears to be corrupt. You can resolve this by running:\n\n  $ rm -rf $1\n  $ yarn install',
   errorDecompressingTarball: '$0. Error decompressing $1, it appears to be corrupt.',
+  updateInstalling: 'Installing $0...',
 };
 
 export type LanguageKeys = $Keys<typeof messages>;
diff --git a/yarn.lock b/yarn.lock
index c21a32f47b..5a7a15d50e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1441,7 +1441,7 @@ concat-map@0.0.1:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
 
-concat-stream@^1.4.6:
+concat-stream@^1.4.6, concat-stream@^1.4.7:
   version "1.5.2"
   resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266"
   dependencies:
@@ -2006,6 +2006,14 @@ extend@^3.0.0, extend@~3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4"
 
+external-editor@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-1.1.0.tgz#c7fe15954b09af852b89aaec82a2707a0dc5597a"
+  dependencies:
+    extend "^3.0.0"
+    spawn-sync "^1.0.15"
+    temp "^0.8.3"
+
 extglob@^0.3.1:
   version "0.3.2"
   resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
@@ -2612,6 +2620,25 @@ ini@^1.3.4, ini@~1.3.0:
   version "1.3.4"
   resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
 
+inquirer:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-1.2.2.tgz#f725c1316f0020e7f3d538c8c5ad0c2732c1c451"
+  dependencies:
+    ansi-escapes "^1.1.0"
+    chalk "^1.0.0"
+    cli-cursor "^1.0.1"
+    cli-width "^2.0.0"
+    external-editor "^1.1.0"
+    figures "^1.3.5"
+    lodash "^4.3.0"
+    mute-stream "0.0.6"
+    pinkie-promise "^2.0.0"
+    run-async "^2.2.0"
+    rx "^4.1.0"
+    string-width "^1.0.1"
+    strip-ansi "^3.0.0"
+    through "^2.3.6"
+
 inquirer@^0.12.0:
   version "0.12.0"
   resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e"
@@ -2766,6 +2793,10 @@ is-primitive@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
 
+is-promise@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
+
 is-property@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
@@ -3668,7 +3699,7 @@ multipipe@^0.1.2:
   dependencies:
     duplexer2 "0.0.2"
 
-mute-stream@~0.0.4:
+mute-stream@~0.0.4, mute-stream@0.0.6:
   version "0.0.6"
   resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.6.tgz#48962b19e169fd1dfc240b3f1e7317627bbc47db"
 
@@ -3903,6 +3934,10 @@ os-locale@^1.4.0:
   dependencies:
     lcid "^1.0.0"
 
+os-shim@^0.1.2:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917"
+
 os-tmpdir@^1.0.0, os-tmpdir@^1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
@@ -4431,10 +4466,21 @@ run-async@^0.1.0:
   dependencies:
     once "^1.3.0"
 
+run-async@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.2.0.tgz#8783abd83c7bb86f41ee0602fc82404b3bd6e8b9"
+  dependencies:
+    is-promise "^2.1.0"
+    pinkie-promise "^2.0.0"
+
 rx-lite@^3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
 
+rx@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
+
 sane@~1.4.1:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/sane/-/sane-1.4.1.tgz#88f763d74040f5f0c256b6163db399bf110ac715"
@@ -4560,6 +4606,13 @@ sparkles@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3"
 
+spawn-sync@^1.0.15:
+  version "1.0.15"
+  resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476"
+  dependencies:
+    concat-stream "^1.4.7"
+    os-shim "^0.1.2"
+
 spdx-correct@~1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40"