I am implementing a component library (an npm module and we'll call it awesome-components) which consists of buttons, inputs, dropdowns etc. where an end user can install it in thier react app and use these components. My confusion is what is the best way to package this library for the end user? I have two approaches in mind,
Using an index.js file to export all the components to be imported as named imports. The folder structure is as follows,
src
|-components
|-Button
|-index.jsx
|-Input
|-index.jsx
|-index.js
index.js will be implemented as follows,
import Button from "./Button";
import Input from "./Input";
export { Button, Input };
The package json's main will point to the index.js in the components root. The npm pack will bundle all the compoenents to one file and will be npm packed and npm publisheded and will look somewhat like this,
dist
|-index.js
|-index.js.map
|-package.json
The end user can import these components in thier app as follows,
import { Button, Input } from "awesome-components";
By not having an index.js in components as follows,
src
|-components
|-Button
|-index.jsx
|-Input
|-index.jsx
And bundling the components seperately and ship to the end user preserving the folder structure which will somehwat look like this,
dist
|-components
|-Button
|-index.js
|-index.js.map
|-Input
|-index.js
|-index.js.map
The end user can use these componenets using absolute path as follows,
import Button from "awesome-library/Button";
import Input from "awesoem-library/Input";
My concern is, which method will benifit the end user ? The number of components can even grow upto 20. What would be the best method to use interms of scalability and performance?
Thanks alot :)
Your first approach is preferable, for four primary reasons.
In your second approach, the paths may change at some point, which would result in a breaking change for all your users.
Submodules from your second approach are usually considered private APIs that external consumers of this package should not rely upon.
Everyone else does it the way you outline it in your first approach.
Several linters, like TSLint, complain when you use submodule imports from external packages.
Related
I have a component library using storybook & TailwindCSS and a host app that's also using TaildwindCSS itself that imports the component library. When the classes are generated, I'm seeing that they're duplicated:
Both projects import TailwindCSS standardly in their index.css files which is then imported into index.tsx using import "./index.css";:
The host app does generate all the classes from the component library when imported but due to there being duplicate classes, some are being overridden due to the order (pay attention to the source and line numbers in the above image)
The component looks correct on storybook:
Host app:
Looking for advice on how to correctly import the component library within the host app?
UPDATE:
I've figured that the component library generates it's own TailwindCSS classes as expected and that's where the "duplicate" classes (inline) come from and it's being included in a single output in index.js in the dist folder. Still need a way to avoid these duplicates when imported in the host app. May need to look at changing the component library to build a separate .css file with the styles and tell the host app to generate the component library's styles to prevent these duplicates.
After reading more on the TailwindCSS documentation, I've found a resolution. Using the following information from https://tailwindcss.com/docs/content-configuration#working-with-third-party-libraries, I was able to fix my issues.
Essentially what I've now done is, on my component library, I ensured that the.css styles are extracted into it's own file and not built into a single index.js. After that, on the host app, I set the content of tailwind config to reference my component library so that it scans the src and generates those classes itself.
The top of most React files has:
import React from 'react'
Yet react is not actually a file. So where is it and how is it being imported from nothing?
When you import from react it first looks into the node_modules/react/index.js like other module looks for the index.js if there's no file specified. And you may also ask why does it look for node_modules? The answer is you have not specified relative or absolute file path for eg. ./components/MyComponent. When you do not specify the specific path, it will look for the node_modules directory.
The index.js exports is like:
if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/react.production.min.js');
} else {
module.exports = require('./cjs/react.development.js');
}
So, let's continue with development environment. Now, when you look into the file node_modules/react/csj/react.development.js, you will find several exports statement at the end of the file.
So, you're simply importing React means you're importing all of them. And thus, you can use React.Component, React.Children, etc.
It's not necessary that you must have named React but it's standard. So, even if you do:
import ReactApplication from 'react'
You have access to all of them like ReactApplication.Component. Hope, this clears up things.
Further details:
When you specify ./, it will look for the current directory.
When you specify /, it will look for the root directory.
When you do not specify, it will first look to directory in your project and if it doesn't find, it will look into the node_modules directory.
Other post you may be interested to look into: https://stackoverflow.com/a/27218926/2138752
React is available as a dependency in the node_modules directory.
React must also be in the scope of files containing JSX to enable transpilers like Babel know how to handle that syntax.
React is installed as an npm package, so it can be found in your node_modules folder. That's where it's being imported from.
I have a requirement which I'm not sure if can be achieved with Rollup (or with Webpack?)
I've written a React component library. Each component imports it's stylesheet. Something like
import "./button.scss"
export default function Button() {
...
}
Now, these scss files use sass variables of all sorts that I would like to define in a global file.
I want the consumer of my library to be able to import Button as so:
import { Button } from "mylib" or even better:
import Button from "mylib/button"
and have only the code required by Button be added to my library.
I can't for the life of me figure/google out how to achieve this.
Is there a good reference to a tree-shakable React component library that uses sass and sass variables?
Bonus question:
I have some third party css that only some of the component require.
So, if each of the component's scss files imports:
#import "~some-third-party-css-lib.css"
Will Rollup duplicate that css? Or is there some kind of deduping mechanism?
Thanks!
From what I know now (Feb 2020) the only options to achieve good tree-shaking results for a component library are:
When using css modules
You need to rely on the consumer of your library to handle the (s)css imports (Create React App for example is doing this pretty well)
Make sure to not transpile/bundle the css in your package and that the import paths are correct
Make the consumers import each component separately (import Button from "mylib/button"). This is because css imports are considered side-effects by most bundlers and it's petty hard (maybe even impossible) to tell which classes are actually used. So if you have an index in your library that re-exports all components, this causes ALL css to be imported and probably bundled.
When using a css-in-js system with a runtime
also make sure that you do not pre-bundle/export the css in the library build-step but rather have that leave that to the consuming app.
This has some implications that should be considered.
The consumer of your components must ship a css-in-js runtime alongside their app
If the consumer is trying to do server-side pre-rendering in some sort that might require custom additional steps depending of the css-in-js library you're using
Conclusion
As of now I don't know a solution that supports full tree-shaking (including css) while also not imposing additional complexity to the consuming app. But I'm currently still investigating this topic and will post updates here if I find something interesting.
Look at the library tailwindcss. It automatically purges the unused css variables.
Can we keep containers-folder outside of the components-folder ?.
And common-folder & helpers-folder inside component-folder ?
How can we arrange the below folders in components-folder ?
containers
pageContainers
common
utils
helpers
To my opinion the "modular" approach is a pretty good for react app. The purpose is to "scope" your files.
For example :
/src
/Components
/Button
index.js
style.css
/Containers
/Dashboard
/Components
...somes components used only in dashboard
index.js
style.css
/UserProfile
/Components
...somes components used only in UserProfile
/Service
fetcher.js
reducer.js
index.js
style.css
/Services
reducer.js
/auth
...
App.js
As you can see we have a "Container" folder Each "module" has his own style, component, services, utils etc...
The benefits of "scoping" your application are:
More easly maintenable
The structure is more clear for the user, you don't have ton search your "Button component" in the only Component folder were you store 100 other components
Evolutive. For exemple you have the "Auth module" wich allow the user to authenticate with your site. If you scope well your component it will be more easy to add some new ffeature for this service/component.
Of Course the general tree of folder will be bigger. You can adapt the structure with your needs but don't forget to think "scope".
This is probably not the "best way". but it has some very good advantages
if you want to go futher i recomand you the excellent article :https://medium.com/#alexmngn/why-react-developers-should-modularize-their-applications-d26d381854c1
and
https://medium.com/#alexmngn/how-to-better-organize-your-react-applications-2fd3ea1920f1
You can use fractal file structure, a comprehensive hackernoon post summarizes it all here.
It makes you reason about the location of files and folders, easier management of components, and paves way for infinite scaling.
I used angularjs(1.x) and new to Vue.There has DI in angular and refer a service in controller or a directive in template is easy, wherever the service or directive is. Angular will help you inject it automatically.
But I realize Vue refer a component by import(ES6) the path where the component in.If I change the component direcotry structure when refactoring(frequently), there will lost the component refer, and I should fix the path one by one. How troublesome it is.
I know there have a vue-injector like angular DI, but if it is easy to use?
Vue doesn't provide any formal dependency injection mechanism. It's completely up to you how you want to import dependencies.
Most Vue code samples do not use dependency injection at all and just use the ES6 module system via import and export.
If I change the component direcotry structure when refactoring(frequently), there will lost the component refer, and I should fix the path one by one. How troublesome it is.
You probably shouldn't be changing the directory structure frequently. Check out the Vue webpack template for an example of how to correctly structure a Vue project.
That being said, it doesn't really answer your question. Are you using webpack (or something similar)? You can configure precisely how webpack should locate modules via the resolve configuration property, so that you don't have to use relative import paths.
Another way is to register each Vue component globally so you don't need to import them at all.
Another way is to abuse ES6 modules by creating, say, a components.js module which imports each Vue component (wherever they are) and exports them all from that one module. Now you only need to import from that central module instead of hunting down the specific module location of each thing you want to import.
// components.js
import Button from 'path/to/button.vue';
import Alert from 'path/to/alert.vue';
export {
Button,
Alert,
};
// myform.js
import { Button } from 'path/to/components.js';
If you change the location of button.vue, you only need to update the import from inside the components.js file.
If you think you are going to restructure a lot, you can have one file that everyone imports and it imports all the rest, that way you only need to change in one place.
I personally prefer the import over the Dependency Injection, way easier to understand where everything is coming from.