4

Goal: I have a simple React component with two imports: react and prop-types and I'm trying to publish it to npm.

import React, { Component } from 'react';
import PropTypes from 'prop-types';

class MyComponent extends Component {
   ...
}

export default MyComponent;

Problem: I've never published anything before, so I'm not sure how to set everything up. Below is what I've attempted - when I try testing it with npm link, I can successfully import the component but as soon as I try using it, it gives me the following error:

Element 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.

File structure:

├── node_modules/
├── lib/
|   ── index.js    <--- this is where webpack builds to
├── src/
|   ── index.js    <--- this is the react component
|
├── package.json
├── webpack.config.js
├── .babelrc
├── .npmignore
├── .gitignore

Package.json:

{
  "name": "...",
  "version": "1.0.0",
  "description": "...",
  "main": "lib/index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "start": "webpack-dev-server --open"
  },
  "repository": {
    "type": "git",
    "url": "..."
  },
  "author": "...",
  "license": "MIT",
  "bugs": {
    "url": "..."
  },
  "homepage": "...",
  "dependencies": {
    "prop-types": "^15.0.0"
  },
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-plugin-transform-class-properties": "^6.24.1",
    "babel-plugin-transform-object-rest-spread": "^6.26.0",
    "babel-preset-env": "^1.6.1",
    "babel-preset-react": "^6.24.1",
    "webpack": "^3.10.0",
  },
  "peerDependencies": {
    "react": "^15.0.0 || ^16.0.0",
    "react-dom": "^15.0.0 || ^16.0.0"
  }
}

webpack file:

const path = require('path');

module.exports = {
  entry:  './src/index.js',
  output: {
    path: path.resolve(__dirname, 'lib'),
    filename: 'index.js'
  },
  module: {
    rules: [
      {
        test: /\.(js)$/,
        use: 'babel-loader'
      }
    ]
  },
  externals: {
    'react': 'commonjs react',
    'react-dom' : 'commonjs react-dom'
  }
};

.babelrc:

{
  "presets": ["env", "react"],
  "plugins": ["transform-class-properties", "transform-object-rest-spread"]
}

Importing:

And, for clarification, I'm not mixing up default and named imports, the package is being imported as a default import:

import MyComponent from 'my-component';
2
  • Can you show how you import the component in the client code? Make sure you import it like this: import MyComponent from 'mycomponent' rather than like this: import { MyComponent } from 'mycomponent' since you're using export default in your module. Commented Jan 10, 2018 at 16:12
  • I'm importing it as a default import Commented Jan 10, 2018 at 16:16

1 Answer 1

1

The webpack config option output.libraryTarget can be used to tell webpack about the type of build it should create:

  • "commonjs2": The return value of your entry point will be assigned to the module.exports
  • "umd": results in a build that works with CommonJS, AMD and old-fashioned script tags/global variables (credit to @JoeClay)

See the documentation here: https://webpack.js.org/configuration/output/#module-definition-systems

These settings will export your component as a CommonJS module that you can import as desired.

Sign up to request clarification or add additional context in comments.

5 Comments

I'd recommend targeting "umd" instead of solely CommonJS - the former results in a build that works with CommonJS, AMD and old-fashioned script tags/global variables.
@forrert Thanks, what's the difference between commonjs and commonjs2? Should I use commonjs2 in my exports section as well?
@JoeClay Thanks, does that mean to add a "umd" property above the commonjs one in libraryTarget and inside externals ?
@linasmnew: Not above - instead of! output.libraryTarget: "umd"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.