DEV Community

Jamie Bradley
Jamie Bradley

Posted on • Updated on

React Splide with Gatsby: Top Tip

If you have never come across SplideJS then I encourage you to check it out. It's a great plugin for building carousels and I think the documentation is brilliant. Check out SplideJS here.

Let me start by telling you about my painful afternoon...

Today, I have been working on a website for a client using Gatsby. Until today, everything has been going great! My task today was to develop a carousel component, so I turned to my favourite tool (Splide) and I noticed they have a React port. "Awesome" I thought, "This is going to be simple!".

Then I noticed an error in the Netlify logs.

  14 |  else
  15 |      root["Splide"] = factory();
> 16 | })(self, function() {
     |  ^
  17 | return /******/ (() => { // webpackBootstrap
  18 | /******/     "use strict";
  19 | /******/     var __webpack_modules__ = ({


  WebpackError: ReferenceError: self is not defined
Enter fullscreen mode Exit fullscreen mode

This doesn't look good.

Debugging Gatsby HTML Builds

There's a great section in the Gatsby documentation about debugging HTML builds here. I consulted this and found the section on third-party modules. I have used this approach with other packages that expect the window object during the HTML build, so I thought I would give this a go.

In gatsby-node.js I added the following:

exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {
  // With SSR enabled during development, we capture this
  // in development and during production build.
  if (stage === "build-html" || stage === "develop-html") {
    actions.setWebpackConfig({
      module: {
        rules: [
          {
            test: /@splidejs/,
            use: loaders.null(),
          },
        ],
      },
    })
  }
}
Enter fullscreen mode Exit fullscreen mode

Great 💥 let's run gatsby build again...oh dear

Error: Minified React error #130; visit https://reactjs.org/docs/error-decoder.html?invariant=130&args[]=undefined&args[]= for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
Enter fullscreen mode Exit fullscreen mode

We have a new error. A new error is good, it means progress! If we copy the error decoder URL into a new browser we can see that React returned the following error.

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.
Enter fullscreen mode Exit fullscreen mode

At first I was puzzled, but after a coffee it made sense. We have told webpack to replace React Splide with a dummy module. The loaders.null() suggests that we're not going to return anything valuable - it's undefined.

So the end result for my carousel component was this:

export const Testimonials = ({ data }) => {
  if (typeof window === "undefined") {
    return <p>Server Render</p>
  }

  return (
    <Box mb={24}>
      <Splide
        options={{
          perPage: 1,
          perMove: 1,
          arrows: false,
        }}
      >
        {data.map(testimonial => {
          return (
            <SplideSlide key={testimonial.id}>
              <Flex bgColor="blue.600">
                <Box width="400px">
                  {/* To be replaced with Gatsby Image */}
                  <img src={testimonial.imageUrl} alt={testimonial.alt} />
                </Box>
                <Box py={12} px={12} display="flex" flex={1} color="white">
                  <PortableText blocks={testimonial._rawTestimonialText} />
                </Box>
              </Flex>
            </SplideSlide>
          )
        })}
      </Splide>
    </Box>
  )
}
Enter fullscreen mode Exit fullscreen mode

This means that when the HTML build takes place, the component will return <p>Server Render</p> rather than undefined. If we run the code in the browser, window is defined and we will see the Splide component.

Conclusion

I wrote this with the hope that it will help anyone else who comes across this problem. If you found it useful then let me know in the comments.

Thanks!

Discussion (17)

Collapse
brunnerlivio profile image
Livio Brunner

Awesome article! I wonder do you know what is the root cause of this? I am currently struggling with this error with an internal third party library which I can easily contribute to. Is something messed up with the build output of that third party library (maybe tweak the Webpack / Rollup config on the libraries side)?

Collapse
brunnerlivio profile image
Livio Brunner

Ah I think Gatsby does not work well with UMD library output. I've changed the third party library to use CommonJS and now I don't need any workarounds.

Here is my rollup config:

import resolve from '@rollup/plugin-node-resolve';
import sourcemaps from 'rollup-plugin-sourcemaps';

export default {
  input: {
    index: 'dist-transpiled/index',
    // 'routing/index': 'dist-transpiled/routing/index'
  },
  output: [
    {
      dir: 'dist/',
      entryFileNames: '[name].esm.js',
      chunkFileNames: '[name]-[hash].esm.js',
      format: 'es',
      sourcemap: true,
    },
    {
      dir: 'dist/',
      format: 'commonjs',
      preferConst: true,
      sourcemap: true,
    },
  ],
  external: (id) => !/^(\.|\/)/.test(id),
  plugins: [
    resolve(),
    sourcemaps(),
  ],
};
Enter fullscreen mode Exit fullscreen mode

The libraries tsconfig.json:

{
  "compilerOptions": {
    "outDir": "dist-transpiled",
    "module": "es2015",
    "lib": ["dom", "es2015"],
    "moduleResolution": "node",
    "jsx": "react",
    "sourceMap": true,
    "declaration": true,
    "esModuleInterop": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "suppressImplicitAnyIndexErrors": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "allowSyntheticDefaultImports": true,
    "target": "ES2015",
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}
Enter fullscreen mode Exit fullscreen mode

and my build command:

tsc -p . && rollup -c

Collapse
jamiebradley profile image
Jamie Bradley Author

Sorry @brunnerlivio I missed your comment. That's a great find, I wasn't sure of the root cause myself but your insight is really useful. Thank you.

Thread Thread
brunnerlivio profile image
Livio Brunner

Worte an article shedding a light on this here:
dev.to/brunnerlivio/use-stencil-wi...

Collapse
marcoadinolfi profile image
Marco Adinolfi • Edited

Hi Jamie, thank you so much for this article!
It's the only one that helped me get Splidejs (great tool) and Gatsby working on the site I was building.
Just one suggestion, with the new SSR Gatsby functionality set to true (and so active) now it's better to update the if statement inside the onCreateWebpackConfig to "if (stage === "build-html" || stage === "develop-html")" so that you don't receive any error during development phase (gatsby develop). At least I had to do it. Maybe you could update the article with this info, it could help someone else.
Thank you so much for your help

Collapse
jamiebradley profile image
Jamie Bradley Author

Sorry for the delayed response Marco! I've had some time away from this site for a bit as I've been busy with day job. Yeah absolutely I'll get this updated and thank you for sharing.

Collapse
zbr_aziz profile image
Zubair Aziz

Hi Jamie, I followed these steps and the build ran fine. But getting this error on the rendered HTML:

[SPLIDE] Track or list was not found.
j @ splide.esm.js:953
2react-dom.production.min.js:216 TypeError: Cannot read property 'length' of undefined
at Object.get length as length
at i.get (splide.esm.js:1838)
at Object.get edgeIndex as edgeIndex
at Object.handler (splide.esm.js:2755)
at splide.esm.js:139
at Array.forEach ()
at Object.emit (splide.esm.js:137)
at i.r.emit (splide.esm.js:1703)
at i.r.refresh (splide.esm.js:1772)
at m.value (Splide.js:208)

Collapse
animald profile image
animald

Hi, if you're still having issues I suggest you try reverting the changes made and importing node-self before your splider import.

The self is not defined error relates to self not being implemented in NodeJs - the above fixes that issue and the result should be a fully working SSR rendered splider. (At least that is the case in my Gridsome project).

Cheers!

Collapse
jamiebradley profile image
Jamie Bradley Author

Thanks @animald , appreciate the input!

Collapse
jamiebradley profile image
Jamie Bradley Author

Sorry for the delay Zubair, interesting as I've not seen this error before. I don't suppose this is related to Marco's comment regarding SSR during dev? I'm only asking with you saying that the build worked fine. I'm reading this as it worked during build phase but not dev phase. Thanks!

Collapse
difaananda40 profile image
M Difa Ananda

Hi Jamie, thanks for the tutorial!
But i follow the step above, and it seems the errors still appear.
Do you have any idea?

Collapse
jamiebradley profile image
Jamie Bradley Author

Oh that's not good (and slightly awkward 😬) which of the errors are you still getting? I'm presuming you mean:

WebpackError: ReferenceError: self is not defined
Enter fullscreen mode Exit fullscreen mode

Let me know. I'm happy to have a look if you want share the code with me, maybe DM me on Twitter(@jamiebradley234)?

Collapse
difaananda40 profile image
M Difa Ananda

Here's my code pastebin.com/m4cB3Rec
Btw, I can't send the dm to your Twitter.

Thread Thread
jamiebradley profile image
Jamie Bradley Author

Thanks, can you send me your twitter handler please and I'll try reaching out to you over Twitter. I've got some questions but I don't want to spam the thread 😂

Thread Thread
difaananda40 profile image
M Difa Ananda
Thread Thread
jamiebradley profile image
Jamie Bradley Author • Edited

Thanks for going through this with me on Twitter! For everyone else's reference the change was to update object in the rules array from:

{
  test: /@splidejs\/react-splide/,
  use: loaders.null(),
}
Enter fullscreen mode Exit fullscreen mode

to

{
  test: /@splidejs/,
  use: loaders.null(),
}
Enter fullscreen mode Exit fullscreen mode

I have updated the post accordingly!

Collapse
mokkapps profile image
Michael Hoffmann

Thanks, I was looking exactly for a solution for that problem!