diff --git a/packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.js b/packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.js
new file mode 100644
index 0000000000000..f9b212012b101
--- /dev/null
+++ b/packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.js
@@ -0,0 +1,150 @@
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @emails react-core
+ */
+
+'use strict';
+
+const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils');
+
+let React;
+let ReactDOMServer;
+
+function initModules() {
+ // Reset warning cache.
+ jest.resetModuleRegistry();
+ React = require('react');
+ ReactDOMServer = require('react-dom/server');
+
+ // Make them available to the helpers.
+ return {
+ ReactDOMServer,
+ };
+}
+
+const {resetModules} = ReactDOMServerIntegrationUtils(initModules);
+
+describe('ReactDOMServerLifecycles', () => {
+ beforeEach(() => {
+ resetModules();
+ });
+
+ it('should invoke the correct lifecycle hooks', () => {
+ const log = [];
+
+ class Outer extends React.Component {
+ unsafe_componentWillMount() {
+ log.push('outer componentWillMount');
+ }
+ render() {
+ log.push('outer render');
+ return ;
+ }
+ }
+
+ class Inner extends React.Component {
+ unsafe_componentWillMount() {
+ log.push('inner componentWillMount');
+ }
+ render() {
+ log.push('inner render');
+ return null;
+ }
+ }
+
+ ReactDOMServer.renderToString();
+ expect(log).toEqual([
+ 'outer componentWillMount',
+ 'outer render',
+ 'inner componentWillMount',
+ 'inner render',
+ ]);
+ });
+
+ it('should warn about deprecated lifecycle hooks', () => {
+ class Component extends React.Component {
+ componentWillMount() {}
+ render() {
+ return null;
+ }
+ }
+
+ expect(() => ReactDOMServer.renderToString()).toWarnDev(
+ 'Warning: Component: componentWillMount() is deprecated and will be removed ' +
+ 'in the next major version. Please use unsafe_componentWillMount() instead.',
+ );
+ });
+
+ it('should update instance.state with value returned from getDerivedStateFromProps', () => {
+ class Grandparent extends React.Component {
+ state = {
+ foo: 'foo',
+ };
+ render() {
+ return (
+
+ {`Grandparent: ${this.state.foo}`}
+
+
+ );
+ }
+ }
+
+ class Parent extends React.Component {
+ state = {
+ bar: 'bar',
+ baz: 'baz',
+ };
+ static getDerivedStateFromProps(props, prevState) {
+ return {
+ bar: `not ${prevState.bar}`,
+ };
+ }
+ render() {
+ return (
+
+ {`Parent: ${this.state.bar}, ${this.state.baz}`}
+ ;
+
+ );
+ }
+ }
+
+ class Child extends React.Component {
+ static getDerivedStateFromProps() {
+ return {
+ qux: 'qux',
+ };
+ }
+ render() {
+ return `Child: ${this.state.qux}`;
+ }
+ }
+
+ const markup = ReactDOMServer.renderToString();
+ expect(markup).toContain('Grandparent: foo');
+ expect(markup).toContain('Parent: not bar, baz');
+ expect(markup).toContain('Child: qux');
+ });
+
+ it('should warn if getDerivedStateFromProps returns undefined', () => {
+ class Component extends React.Component {
+ static getDerivedStateFromProps() {}
+ render() {
+ return null;
+ }
+ }
+
+ expect(() => ReactDOMServer.renderToString()).toWarnDev(
+ 'Component.getDerivedStateFromProps(): A valid state object (or null) must ' +
+ 'be returned. You may have returned undefined.',
+ );
+
+ // De-duped
+ ReactDOMServer.renderToString();
+ });
+});
diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js
index 6ca0ff4ffb116..144c59932f0ca 100644
--- a/packages/react-dom/src/server/ReactPartialRenderer.js
+++ b/packages/react-dom/src/server/ReactPartialRenderer.js
@@ -123,6 +123,7 @@ let didWarnDefaultTextareaValue = false;
let didWarnInvalidOptionChildren = false;
const didWarnAboutNoopUpdateForComponent = {};
const didWarnAboutBadClass = {};
+const didWarnAboutUndefinedDerivedState = {};
const valuePropNames = ['value', 'defaultValue'];
const newlineEatingTags = {
listing: true,
@@ -421,6 +422,33 @@ function resolve(
if (shouldConstruct(Component)) {
inst = new Component(element.props, publicContext, updater);
+
+ if (typeof Component.getDerivedStateFromProps === 'function') {
+ partialState = Component.getDerivedStateFromProps(
+ element.props,
+ inst.state,
+ );
+
+ if (__DEV__) {
+ if (partialState === undefined) {
+ const componentName = getComponentName(Component) || 'Unknown';
+
+ if (!didWarnAboutUndefinedDerivedState[componentName]) {
+ warning(
+ false,
+ '%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' +
+ 'You may have returned undefined.',
+ componentName,
+ );
+ didWarnAboutUndefinedDerivedState[componentName] = true;
+ }
+ }
+ }
+
+ if (partialState != null) {
+ inst.state = Object.assign({}, inst.state, partialState);
+ }
+ }
} else {
if (__DEV__) {
if (