diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js
index b03f9ed36425b..f50253904d698 100644
--- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js
@@ -871,5 +871,71 @@ describe('ReactDOMServerIntegration', () => {
: ''),
);
});
+
+ describe('badly-typed elements', function() {
+ itThrowsWhenRendering(
+ 'object',
+ async render => {
+ let EmptyComponent = {};
+ expect(() => {
+ EmptyComponent = ;
+ }).toWarnDev(
+ 'Warning: React.createElement: type is invalid -- expected a string ' +
+ '(for built-in components) or a class/function (for composite ' +
+ 'components) but got: object. You likely forgot to export your ' +
+ "component from the file it's defined in, or you might have mixed up " +
+ 'default and named imports.',
+ );
+ await render(EmptyComponent);
+ },
+ 'Element type is invalid: expected a string (for built-in components) or a class/function ' +
+ '(for composite components) but got: object.' +
+ (__DEV__
+ ? " You likely forgot to export your component from the file it's defined in, " +
+ 'or you might have mixed up default and named imports.'
+ : ''),
+ );
+
+ itThrowsWhenRendering(
+ 'null',
+ async render => {
+ let NullComponent = null;
+ expect(() => {
+ NullComponent = ;
+ }).toWarnDev(
+ 'Warning: React.createElement: type is invalid -- expected a string ' +
+ '(for built-in components) or a class/function (for composite ' +
+ 'components) but got: null.',
+ );
+ await render(NullComponent);
+ },
+ 'Element type is invalid: expected a string (for built-in components) or a class/function ' +
+ '(for composite components) but got: null',
+ );
+
+ itThrowsWhenRendering(
+ 'undefined',
+ async render => {
+ let UndefinedComponent = undefined;
+ expect(() => {
+ UndefinedComponent = ;
+ }).toWarnDev(
+ 'Warning: React.createElement: type is invalid -- expected a string ' +
+ '(for built-in components) or a class/function (for composite ' +
+ 'components) but got: undefined. You likely forgot to export your ' +
+ "component from the file it's defined in, or you might have mixed up " +
+ 'default and named imports.',
+ );
+
+ await render(UndefinedComponent);
+ },
+ 'Element type is invalid: expected a string (for built-in components) or a class/function ' +
+ '(for composite components) but got: undefined.' +
+ (__DEV__
+ ? " You likely forgot to export your component from the file it's defined in, " +
+ 'or you might have mixed up default and named imports.'
+ : ''),
+ );
+ });
});
});
diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js
index 656f20fc7144a..e774aeaace83d 100644
--- a/packages/react-dom/src/server/ReactPartialRenderer.js
+++ b/packages/react-dom/src/server/ReactPartialRenderer.js
@@ -889,12 +889,33 @@ class ReactDOMServerRenderer {
break;
}
}
+
+ let info = '';
+ if (__DEV__) {
+ const owner = nextElement._owner;
+ if (
+ elementType === undefined ||
+ (typeof elementType === 'object' &&
+ elementType !== null &&
+ Object.keys(elementType).length === 0)
+ ) {
+ info +=
+ ' You likely forgot to export your component from the file ' +
+ "it's defined in, or you might have mixed up default and " +
+ 'named imports.';
+ }
+ const ownerName = owner ? getComponentName(owner) : null;
+ if (ownerName) {
+ info += '\n\nCheck the render method of `' + ownerName + '`.';
+ }
+ }
invariant(
false,
'Element type is invalid: expected a string (for built-in ' +
'components) or a class/function (for composite components) ' +
- 'but got: %s.',
+ 'but got: %s.%s',
elementType == null ? elementType : typeof elementType,
+ info,
);
}
}