Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Empty theme context for 'external' components #2379

Closed
adriankremer opened this issue Feb 11, 2019 · 23 comments
Closed

Empty theme context for 'external' components #2379

adriankremer opened this issue Feb 11, 2019 · 23 comments

Comments

@adriankremer
Copy link

adriankremer commented Feb 11, 2019

Using a ThemeProvider with a theme, components that are separate transpiled modules (in my case UMD) inheriting {props => props.theme..} won't work. Also tried with HoC (withTheme) where i get theme: undefined.
They're just empty.

App

import React, { Component } from 'react';
import { ThemeProvider } from 'styled-components';
import Theme from '@namics-ui/theme-blue';
import Button from './Button';
import StyledButton from '@namics-ui/test';

class App extends Component {
  render() {
    return (
	<ThemeProvider theme={Theme}>
	         <div>
		     <Button text="bla" />  // can access the theme props
		     <StyledButton text="blabla" /> // cannot access the theme props
		</div>
	</ThemeProvider>
    );
  }
}

export default App;

Component (StyledButton)

import React, { ComponentType } from 'react';
import styled, { withTheme } from 'styled-components';

interface ButtonProps {
	text?: string;
	children?: any;
}

const StyledButton = styled.button`
	background-color: ${props => props.theme.colors.main[500]};
`;

const Button: React.StatelessComponent<ButtonProps> = (props) => {
	console.log(props);
  return (
    <StyledButton>{props.text}</StyledButton>
  );
};

export default withTheme(Button as ComponentType<any>);

Tried with NPM and Yarn.
I thought this worked before.

System:

  • OS: macOS 10.14.3
  • CPU: (12) x64 Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
  • Memory: 714.37 MB / 16.00 GB
  • Shell: 3.2.57 - /bin/bash

Binaries:

  • Node: 11.9.0 - ~/.nvm/versions/node/v11.9.0/bin/node
  • Yarn: 1.13.0 - /usr/local/bin/yarn
  • npm: 6.5.0 - ~/.nvm/versions/node/v11.9.0/bin/npm

npmPackages:

  • styled-components: ^4.0.0 => 4.1.3

Steps to reproduce

The easiest way to reproduce this is to setup a lerna monorepo and to add a 'app' that uses a ThemeProvider and a separate 'component' package that invokes ${props => props.theme}.

@adriankremer
Copy link
Author

Goddammit i solved it. I studied the approach from @reakit and learned that importing styled-component twice causes different instances (importing for me is not attached to creating instances, maybe thats the reason it was so hard for me to understand).

So i solved it by creating a package that imports and exports styled-components like

import { ThemeProvider } from 'styled-components';
export { ThemeProvider }

and then reuse this package in the App like

import { ThemeProvider } from 'my-own-package';

after this the theme prop is filled with the theme props from the ThemeProvider.

@Iulia-Soimaru
Copy link

I have exact same problem, theme is in the context for all the internal components, but in external components theme is not defined.
I tried your approach @adriankremer, but importing and exporting ThemeProvider in separate file didn't really solve the issue for me :(

Is there anything else you did to make it work?

@conways-glider
Copy link

@probablyup - The solution provided by @adriankremer did not work for me. Could we reopen this issue?

@conways-glider
Copy link

Actually - this helped my issue: https://www.styled-components.com/docs/faqs#how-can-i-fix-issues-when-using-npm-link-or-yarn-link

@JeuelyFish
Copy link

JeuelyFish commented Jun 14, 2019

I'm in a similar situation and, like @Iulia-Soimaru, the proposed solution to create a file that imports and exports ThemeProvider didn't work.

I then tried the proposed solution of adjusting my webpack config with something like

{
    resolve: {
      alias: {
        'my-external-components': path.resolve('../path-to-my-external/components/')
      },
    }
  }

and also didn't get any further along. My theme object in the external components props is still empty. Is there anything more specific that you did @Fluffy-Samurai ?

@quantizor
Copy link
Contributor

If you're using yarn, the "resolutions" package.json field is usually more reliable than the webpack alias in my experience

@Iulia-Soimaru
Copy link

@JeuelyFish I fixed this issue by wrapping external component in ThemeProvider and passing custom theme as a prop.

Component from external package:

const ExternalComponent = (props) => (
    <ThemeProvider theme={contextTheme => contextTheme || props.theme}>
        <MyComponent {...props} />
    </ThemeProvider>
);

export default ExternalComponent;

using external component in my application:

<ExternalComponent
    {...props}
    theme={customTheme}
/>

@nlicitra
Copy link

nlicitra commented Jul 1, 2019

For those that may be having issues with their own external library, the following worked for me.

Make styled-components an external dependency of your component library. (Documentation). This ensures that your component library uses the same instance of the styled-components package that your application is using, thus allowing all components (local and external) to be properly provided themes from the ThemeProvider.

@hutber
Copy link

hutber commented Feb 10, 2020

You beautiful golden child @nlicitra!!! Indeed, this was the fix for myself.
I tried all other suggestions before this one. Curse you for being last :P

@pclnryankim
Copy link

None of these suggestions worked for me but here's what did:

Component's Library:

npm link react 
npm link styled-components
npm link
npm run watch 

Application:

npm link react
npm link styled-components
npm link <Component Library Name>
npm start

This forces both applications to use the linked version which SHOULD be the same. I wrote a little script for myself to run these commands on the projects so it's not as hacky as it seems.

@appsparkler
Copy link

👍 @nlicitra, @Fluffy-Samurai

I'm faced this issue in a monorepo.

There were multiple packages which had different versions of styled-components.

I set the same version of styled-components for each package depending on styled-components

package-1

"styled-components": "^4.2.1",

packages-2

"styled-components": "^5.0.0"

I updated all to something like this:

"styled-components": "^5.0.0"

@ghost
Copy link

ghost commented Aug 9, 2020

Still facing the same issue here: https://stackoverflow.com/questions/63327130/undefined-props-theme-when-using-local-component-library-in-another-application

Tried the solutions above but nothing seems to work, would appreciate if someone can see if I'm dong something wrong in my setup.

@monokrome
Copy link

Not sure why this was closed... It's definitely still an issue.

@appsparkler
Copy link

@carrein,

Can you create a repository on GitHub replicating the issue?

This way we can clone and debug.

Thanks!

@farzd
Copy link

farzd commented Feb 22, 2021

I got it working. I moved styled-components as a peer dep [and into dev deps if you want to run your other package on its own, i.e with storybook etc] and i updated webpack to look for style-components as a dep in the original package

my webpack config

var path = require('path');

module.exports = {
    mode: 'development',
    entry: './index.tsx',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'index.js',
        library: 'vc-components',      
        libraryTarget: 'umd',      
        publicPath: '/dist/',      
        umdNamedDefine: true  

    },
    resolve: {
        extensions: ['.ts', '.tsx', '.js', '.json'], 
        alias: {          
            'react': path.resolve(__dirname, './node_modules/react'),
            'react-dom': path.resolve(__dirname, './node_modules/react-dom'),
            'styled-components': path.resolve(__dirname, './node_modules/styled-components'),
        }  
    },  
    externals: {      
        // Don't bundle react or react-dom      
        react: {          
            commonjs: "react",          
            commonjs2: "react",          
            amd: "React",          
            root: "React"      
        },      
        "react-dom": {          
            commonjs: "react-dom",          
            commonjs2: "react-dom",          
            amd: "ReactDOM",          
            root: "ReactDOM"      
        },
        "styled-components": {
            commonjs: 'styled-components',
            commonjs2: 'styled-components',
            amd: 'styled-components',
            root: 'styled'
        }
    },
    module: {
        rules: [{
            // Include ts, tsx, js, and jsx files.
            test: /\.(ts|js)x?$/,
            exclude: [
                /node_modules/,
                /stories/,
                /.storybook/
              ],
            loader: 'babel-loader',
        },{
            test: /\.(png|jpe?g|gif|svg)$/i,
            use: [
              {
                loader: 'url-loader',
              },
            ],
          }
        ]
    }
};

package.json

  "peerDependencies": {
    "react": "^17.0.1",
    "react-dom": "^17.0.1",
    "styled-components": "^5.2.1"
  },

@barucAlmaguer
Copy link

@monokrome I think this is not an issue with styled-components itself, but with npm / package.json configurations.

If you have dependencies which rely on styled-components, all those dependencies should include styled-components as a peerDependency in the package.json

only the final app should include styled-components as a regular dependency.

This should fix the issues, but I think there is nothing code-wise to do on the styled-components side.

maybe just be more clear with this in the docs

@j-avila
Copy link

j-avila commented Dec 30, 2021

I'm having a similar issue.
I have a library which uses styled components (as peerDependency) and in the project itself, but when I want to use some theme prop in an component of the app (not the library) the context is undefined, but the components in the library does have the theme in context

some styled component created in app:

image

button in library
image

the code:
Screen Shot 2021-12-30 at 17 26 29

@abelgibayev
Copy link

abelgibayev commented Apr 1, 2022

j-avila, I had the same issue as yours one. Monorepo with lerna, shared module accepts styled-components as peerDependency from other packages. The problem was resolved by setting styled-components to the same version (^5.3.3 in my case) in all packages.

@asgvard
Copy link

asgvard commented Sep 9, 2022

Had the same issue. Using monorepo with multiple UI components that has to be customized through a shared theme.
For me setting same versions in the peerDeps didn't help, Lerna was still installing styled-components for each package.
However I found the --hoist flag, which actually enables the feature where Lerna detects shared deps and puts them into a root folder, which is exactly what is needed for this :)

lerna bootstrap --hoist

@saurabhnemade
Copy link

If anyone is still facing issues with Lerna then the following worked for me:

  • Remove all styled-components from dependencies & devDependencies of all packages.
  • Add styled-components to root package JSON's dependencies & devDependencies
  • If your package is going deployed to the artifactory & used as a separate package anywhere outside the monorepo, specify peerDependencies in such packages and specify ranges to avoid pinning dependencies to a specific version in the consumer app.

@lancejpollard
Copy link

lancejpollard commented Jan 14, 2023

Solved for Next.js project using a separate standalone package which uses Next.js, styled-components, and react. Solved it based on all the wonderful feedback and solutions in this thread.

  1. Specify styled-components and other "complex" packages as peerDependencies and devDependencies in the standalone module. Don't install yet.
  2. Add them as regular dependencies in the main app.
  3. Now npm link all these special packages in both repos.
  4. I npm run watch to build the TS standalone package on file change.
  5. Tell webpack to alias those modules to the one in the app.

Here is my next.config.js which aliases in webpack config:

/** @type {import('next').NextConfig} */
import createMDXPlugin from '@next/mdx'
import path from 'path'
import { fileURLToPath } from 'url'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

const mdxConfig = {
  extension: /\.mdx?$/,
  options: {
    providerImportSource: '@mdx-js/react',
    rehypePlugins: [],
    // If you use remark-gfm, you'll need to use next.config.mjs
    // as the package is ESM only
    // https://github.com/remarkjs/remark-gfm#install
    remarkPlugins: [],
  },
}

const withMDX = createMDXPlugin(mdxConfig)

const nextConfig = withMDX({
  compiler: {
    styledComponents: true,
  },
  images: {
    domains: ['lancejpollard.com'],
  },
  pageExtensions: ['mdx', 'md', 'tsx', 'ts'],
  poweredByHeader: false,
  // reactStrictMode: true,
  trailingSlash: false,
  webpack: config => {
    config.resolve = {
      ...config.resolve,
      alias: {
        ...config.resolve.alias,
        next: path.resolve(__dirname, './node_modules/next'),
        react: path.resolve(__dirname, './node_modules/react'),
        'react-dom': path.resolve(
          __dirname,
          './node_modules/react-dom',
        ),
        'styled-components': path.resolve(
          __dirname,
          './node_modules/styled-components',
        ),
      },
      extensions: ['.ts', '.tsx', '.js', '.json'],
    }

    return config
  },
})

export default nextConfig

Now can develop the standalone package while using the app and refresh the browser, and it reflects the changes!

Thank you so much everyone for the help 🥳

Now onto other issues with module loading in this weird setup :)

  • Now VSCode is importing two reacts when it autocompletes.
  • styled-components className differs on the server potentially than the client.
  • Yay.

But it is working for the most part otherwise.

@limaAniceto
Copy link

I'd just like to share in our case (monorepo), we had to apply a combination of the above solutions (and not just one):

  1. Mark styled-components as external (docs)
  2. In the webpack config, add the alias to the location of the node_modules of styled components like (JeulyFish said - here)

Note that for us on (2) it was on the directory above of the current package - i.e. ../node_modules/styled-components

Hope this is helpful to someone in the future 👍

adghayes added a commit to votingworks/vxsuite that referenced this issue Aug 4, 2023
adghayes added a commit to votingworks/vxsuite that referenced this issue Aug 4, 2023
adghayes added a commit to votingworks/vxsuite that referenced this issue Aug 4, 2023
adghayes added a commit to votingworks/vxsuite that referenced this issue Aug 4, 2023
adghayes added a commit to votingworks/vxsuite that referenced this issue Aug 4, 2023
* minor upgrade: styled-components

* remap package in jest setup

* treat styled-components as peer dependency due to risk of having two theming environments.

reference: styled-components/styled-components#2379
@OmerGery
Copy link

Upgrading style components to 5.x.x + fixed this for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests