I guess something is wrong with the rollup.config.js file. When doing:
import { Button } from 'react-md';
it looks like the full library is still being imported. The bundle size does not change even when more components are imported.

I am also looking for a solution to this problem. Maybe someone has an idea!
Yeah, I'm not sure what to do. I have an update where my build is similar to react-router's, but it still looks like the unused code is being bundled for both. /shrug
Hey guys, I'm struggling with my bundle size too. A temporary solution I found is to use babel-plugin-transform-imports ... that way you can use the namespaced imports and the plugin will take care of transforming them to deep imports considerably reducing the bundle size.
For my current components I'm using the following transform function which you might like to extend according to your needs:
module.exports = function transformImports(importName) {
const reactMD = "react-md/lib";
switch (importName) {
// Cards
case "CardActions":
case "CardText":
case "CardTitle":
return `${reactMD}/Cards/${importName}`;
// DataTables
case "TableHeader":
case "TableCardHeader":
case "TableBody":
case "TableRow":
case "TableColumn":
return `${reactMD}/DataTables/${importName}`;
// Inks
case "injectInk":
return `${reactMD}/Inks/${importName}`;
// Tabs
case "TabsContainer":
case "Tabs":
case "Tab":
return `${reactMD}/Tabs/${importName}`;
// Lists
case "ListItem":
return `${reactMD}/Lists/${importName}`;
// ExpansionPanels
case "ExpansionList":
return `${reactMD}/ExpansionPanels/${importName}`;
// Dialogs
case "DialogContainer":
return `${reactMD}/Dialogs/${importName}`;
// Pickers
case "DatePicker":
return `${reactMD}/Pickers/${importName}`;
// Progress
case "CircularProgress":
return `${reactMD}/Progress/${importName}`;
// SelectionControls
case "Checkbox":
return `${reactMD}/SelectionControls/${importName}`;
default:
return `${reactMD}/${importName}s/${importName}`;
}
};
@hisapy I stumbled across the same thing today but didn't investigate any further but also wanted to build a custom import transformer for react-md. Will grab your code and extend it further to my needs, thanks!
Currently react-md is the largest dependency regarding bundle size (~1.25MB).
I got to a point where waiting for re-compiling and reloading the page was a PITA due to large bundles. Now I'm back on track ... the good stuff about this is that once the tree-shaking stuff is fixed we can simply remove the plugin from our webpack config
Do I understand this correctly: If I switch all my import { List } from 'react-md'; lines to import List from 'react-md/Lists/List' (which is what IntelliJ tries to import in typescript), I might be able to cut down on the bundle size and the time webpack takes to compile my app?
These 30s developer webpack builds are killing me. It used to be fast, but around the time react-md switched to rollup the build time went haywire.
yes @stickfigure. That trick with the babel-plugin-transform-imports and of course a good code splitting. In my case, in development my bundle size was ~8.5 MB due to source maps. Besides those large bundles I had some _app_ modules inside them so every time I saved a file the bundle was compiled.
ls -lh node_modules/react-md/es/react-md.js gives me
-rw-r--r-- 1 hisa staff 1.4M Dec 4 13:47 node_modules/react-md/es/react-md.js
I think the react-md build should be an index file that exports each of the components and not a bundle containing all the lib's modules.
A library with tree-shaking working ok is "react-router": "^3.2.0",.
import { Route, IndexRoute } from "react-router";
results in a smaller bundle size than
import Route from 'react-router/lib/Route';
import IndexRoute from 'react-router/lib/IndexRoute';
With the tricks mentioned my vendor-base.js bundle is now 5.91MB ... it is the largest in my output but it only contains vendor code so it doesn't need to be updated each time a save a file in my app.
I've created a small package with a transformer for most components: https://github.com/Kaishiyoku/webpack-react-md-import-transformer
Please read the installation instructions carefully.
IMHO I think that transforming imports is not actually _tree-shaking_, but overall, your lib looks good @Kaishiyoku
@hisapy You're right! I updated the README.
Thanks everyone! I replaced all the "react-md" imports with specific file imports and my build went from 30+s to sub 10s, sometimes even 5s. MUCH better. It was tedious to do by hand in a large project but the IDE did most of the work.
Cool @stickfigure ... I was doing that but since I'm a lazy developer I looked for a pluggable solution (like babel-plugin-lodash) so then I started writing my first babel plugin. Luckily, after some researching/reading I found the babel-plugin-transform-imports. Also, I remembered how tedious was rewriting the deep imports to namespaced imports when upgraded react-md to the version with tree-shaking
I'd suggest you try the https://github.com/Kaishiyoku/webpack-react-md-import-transformer created by @Kaishiyoku . In the future, when the tree-shaking gets fixed you'll just have to unplug the the plugin to try tree-shaking again.
unfortunately I'm using create-react-app so I can't seem to use the babel build.
maybe i should just go back to the individual import lines for now.
alas I can't figure out how to get it to work with my own setup, but in case anyone else is using hisapy's setup i put together a (i think) complete version of that switch statement:
module.exports = function transformImports(importName) {
const reactMD = "react-md/lib";
switch (importName) {
// Autocompletes
case "Autocomplete":
return `${reactMD}/Autocompletes/${importName}`;
// Avatars
case "Avatar":
return `${reactMD}/Avatars/${importName}`;
// Badges
case "Badge":
return `${reactMD}/Badges/${importName}`;
// Bottom Navigations
case "BottomNav":
case "BottomNavigation":
return `${reactMD}/BottomNavigations/${importName}`;
// Buttons
case "Button":
case "FlatButton":
case "FloatingButton":
case "IconButton":
case "RaisedButton":
return `${reactMD}/Buttons/${importName}`;
// Cards
case "Card":
case "CardActionOverlay":
case "CardActions":
case "CardExpander":
case "CardMedia":
case "CardText":
case "CardTitle":
case "CardTitleBlock":
return `${reactMD}/Cards/${importName}`;
// Chips
case "Chip":
return `${reactMD}/Chips/${importName}`;
// DataTables
case "DataTable":
case "DropdownMenuColumn":
case "EditDialog":
case "EditDialogColumn":
case "MenuButtonColumn":
case "SelectFieldColumn":
case "TableBody":
case "TableCardHeader":
case "TableCheckbox":
case "TableColumn":
case "TableFooter":
case "TableHeader":
case "TablePagination":
case "TableRow":
return `${reactMD}/DataTables/${importName}`;
// Dialogs
case "Dialog":
case "DialogContainer":
case "DialogFooter":
case "DialogTitle":
return `${reactMD}/Dialogs/${importName}`;
// Dividers
case "Divider":
return `${reactMD}/Dividers/${importName}`;
// Drawers
case "Drawer":
case "DrawerTypes":
case "Overlay":
return `${reactMD}/Drawers/${importName}`;
// ExpansionPanels
case "ExpansionList":
case "ExpansionPanel":
case "PanelContent":
return `${reactMD}/ExpansionPanels/${importName}`;
// FABTransitions
case "SpeedDial":
return `${reactMD}/FABTransitions/${importName}`;
// File Inputs
case "FileInput":
case "FileUpload":
return `${reactMD}/FileInputs/${importName}`;
// Font Icons
case "FontIcon":
return `${reactMD}/FontIcons/${importName}`;
// Grids
case "Cell":
case "Grid":
case "GridList":
return `${reactMD}/Grids/${importName}`;
// Helpers
case "AccessibleFakeButton":
case "AccessibleFakeInkedButton":
case "Collapse":
case "FocusContainer":
case "HorizontalAnchors":
case "IconSeperator":
case "Layover":
case "Portal":
case "Positions":
case "ResizeObserver":
case "VerticalAnchors":
case "anchorShape":
case "fixedToShape":
case "positionShape":
return `${reactMD}/Helpers/${importName}`;
// Inks
case "Ink":
case "InkContainer":
case "injectInk":
return `${reactMD}/Inks/${importName}`;
// Lists
case "List":
case "ListItem":
case "ListItemControl":
case "ListItemText":
case "TileAddon":
return `${reactMD}/Lists/${importName}`;
// Media
case "Media":
case "MediaOverlay":
return `${reactMD}/Media/${importName}`;
// Menus
case "DropdownMenu":
case "Menu":
case "MenuButton":
return `${reactMD}/Menus/${importName}`;
// Navigation Drawers
case "CloseButton":
case "JumpToContentLink":
case "MiniListItem":
case "NavigationDrawer":
return `${reactMD}/NavigationDrawers/${importName}`;
// Papers
case "Paper":
return `${reactMD}/Papers/${importName}`;
// Pickers
case "CalendarDate":
case "CalendarHeader":
case "CalendarMonth":
case "ClockFace":
case "ClockHand":
case "ClockTime":
case "DatePicker":
case "DatePickerCalendar":
case "DatePickerContainer":
case "DatePickerHeader":
case "PickerControl":
case "PickerFooter":
case "TimePeriods":
case "TimePicker":
case "TimePickerContainer":
case "TimePickerHeader":
case "Year":
case "YearPicker":
return `${reactMD}/Pickers/${importName}`;
// Progress
case "CircularProgress":
case "LinearProgress":
return `${reactMD}/Progress/${importName}`;
// Select Fields
case "SelectField":
case "SelectFieldInput":
case "SelectFieldToggle":
return `${reactMD}/SelectFields/${importName}`;
// SelectionControls
case "Checkbox":
case "Radio":
case "RadioGroup":
case "SelectionControl":
case "SelectionControlGroup":
case "Switch":
case "SwitchThumb":
case "SwitchTrack":
return `${reactMD}/SelectionControls/${importName}`;
// Sidebars
case "Sidebar":
return `${reactMD}/Sidebars/${importName}`;
// Sliders
case "DiscreteValue":
case "Slider":
case "SliderLabel":
case "Thumb":
case "ThumbMask":
case "Track":
case "TrackFill":
return `${reactMD}/Sliders/${importName}`;
// Snackbars
case "Snackbar":
case "SnackbarContainer":
return `${reactMD}/Snackbars/${importName}`;
// Subheaders
case "Subheader":
return `${reactMD}/Subheaders/${importName}`;
// SVG Icons
case "SVGIcon":
return `${reactMD}/SVGIcons/${importName}`;
// Tabs
case "MenuTab":
case "Tab":
case "TabIndicator":
case "TabOverflowButton":
case "TabPanel":
case "Tabs":
case "TabsContainer":
return `${reactMD}/Tabs/${importName}`;
// Text Fields
case "FloatingLabel":
case "InputField":
case "Message":
case "PasswordButton":
case "TextArea":
case "TextField":
case "TextFieldDivider":
case "TextFieldMessage":
return `${reactMD}/TextFields/${importName}`;
// Toolbars
case "Toolbar":
case "ToolbarTitle":
return `${reactMD}/Toolbars/${importName}`;
// Tooltips
case "Tooltip":
case "TooltipContainer":
case "Tooltipped":
case "injectTooltip":
return `${reactMD}/Tooltips/${importName}`;
// constants
case "CSSTransitionGroupTick":
case "additionalInkTriggerKeys":
case "keyCodes":
case "media":
return `${reactMD}/constants/${importName}`;
default:
return `${reactMD}/${importName}s/${importName}`;
}
};
Hey devs ..
I just wanted to post an update with perhaps valid info regarding this issue

The screenshot is from an app I've just created with:
react-md 1.9.0
The only import from react-md is
import { NavigationDrawer } from "react-md";
So we can confirm that the tree-shaking is still not working. Following are the stats of react-md in the bundle:
Looking at this stats, imagine the reduction with can achieve with _dead code elimination_.
See webpack-bundle-analyzer Size definitions for more info.
It looks like adding sideEffects to the package.json will make tree shaking work with Webpack, according to https://webpack.js.org/guides/tree-shaking#mark-the-file-as-side-effect-free and to some tests I made.
I'm not sure though if react-md has some "effectful" files that need to be excluded.
Most helpful comment
yes @stickfigure. That trick with the
babel-plugin-transform-importsand of course a good code splitting. In my case, in development my bundle size was ~8.5 MB due to source maps. Besides those large bundles I had some _app_ modules inside them so every time I saved a file the bundle was compiled.ls -lh node_modules/react-md/es/react-md.jsgives me-rw-r--r-- 1 hisa staff 1.4M Dec 4 13:47 node_modules/react-md/es/react-md.jsI think the react-md build should be an index file that exports each of the components and not a bundle containing all the lib's modules.
A library with tree-shaking working ok is
"react-router": "^3.2.0",.results in a smaller bundle size than
With the tricks mentioned my vendor-base.js bundle is now 5.91MB ... it is the largest in my output but it only contains vendor code so it doesn't need to be updated each time a save a file in my app.