A Frontend White Screen Incident

· 3 min read · 506 Words · -Views -Comments

In dev the app rendered a white screen with You should not use <Link> outside a <Router>.

It looked like a React Router issue, yet the app worked locally—so the build had to be different. What exactly?

Hypothesis

Two suspects: dependency versions or build scripts. Recent commits didn’t touch the scripts, so versions felt likelier. We had edited package.json recently without changing react-router, so maybe CI installed a mismatched version anyway.

Our front end builds via Maven: the plugin runs the Node build and bundles everything into a WAR.

Inspect POM.xml

  <plugin>
                        <groupId>com.github.eirslett</groupId>
                        <artifactId>frontend-maven-plugin</artifactId>
                        <executions>
                            <execution>
                                <id>install node and npm</id>
                                <goals>
                                    <goal>install-node-and-npm</goal>
                                </goals>
                                <configuration>
                                    <nodeVersion>${node.version}</nodeVersion>
                                    <npmVersion>${npm.version}</npmVersion>
                                    <nodeDownloadRoot>http://cdn.npm.taobao.org/dist/node/</nodeDownloadRoot>
                                </configuration>
                            </execution>
                            <execution>
                                <id>npm config</id>
                                <goals>
                                    <goal>npm</goal>
                                </goals>
                                <configuration>
                                    <arguments>config set registry https://xx.cn/repository/npm/
                                    </arguments>
                                </configuration>
                            </execution>
                            <execution>
                                <id>npm install</id>
                                <goals>
                                    <goal>npm</goal>
                                </goals>
                                <configuration>
                                    <arguments>install --registry=https://xx.cn/repository/npm/</arguments>
                                </configuration>
                            </execution>
                            <execution>
                                <id>webpack build test</id>
                                <goals>
                                    <goal>npm</goal>
                                </goals>
                                <phase>test</phase>
                                <configuration>
                                    <arguments>run webpack:test</arguments>
                                    <npmInheritsProxyConfigFromMaven>false</npmInheritsProxyConfigFromMaven>
                                </configuration>
                            </execution>
                            <execution>
                                <id>webpack build prod</id>
                                <goals>
                                    <goal>npm</goal>
                                </goals>
                                <phase>generate-resources</phase>
                                <configuration>
                                    <arguments>run webpack:prod</arguments>
                                    <npmInheritsProxyConfigFromMaven>false</npmInheritsProxyConfigFromMaven>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>

CI used npm install. Locally we run Yarn, which respects yarn.lock. Without the lock file, npm resolved slightly different versions—hence the runtime error.

Fix

Switch the Maven build to Yarn:

 <plugin>
                        <groupId>com.github.eirslett</groupId>
                        <artifactId>frontend-maven-plugin</artifactId>
                        <executions>
                            <execution>
                                <id>install node and yarn</id>
                                <goals>
                                    <goal>install-node-and-yarn</goal>
                                </goals>
                                <configuration>
                                    <nodeVersion>${node.version}</nodeVersion>
                                    <yarnVersion>${yarn.version}</yarnVersion>
                                </configuration>
                            </execution>
                            <execution>
                                <id>yarn config</id>
                                <goals>
                                    <goal>yarn</goal>
                                </goals>
                                <configuration>
                                    <arguments>config set registry https://xx.cn/repository/npm/
                                    </arguments>
                                </configuration>
                            </execution>
                            <execution>
                                <id>yarn install</id>
                                <goals>
                                    <goal>yarn</goal>
                                </goals>
                            </execution>
                            <execution>
                                <id>webpack build test</id>
                                <goals>
                                    <goal>yarn</goal>
                                </goals>
                                <phase>test</phase>
                                <configuration>
                                    <arguments>run webpack:test</arguments>
                                    <yarnInheritsProxyConfigFromMaven>false</yarnInheritsProxyConfigFromMaven>
                                </configuration>
                            </execution>
                            <execution>
                                <id>webpack build prod</id>
                                <goals>
                                    <goal>yarn</goal>
                                </goals>
                                <phase>generate-resources</phase>
                                <configuration>
                                    <arguments>run webpack:prod</arguments>
                                    <yarnInheritsProxyConfigFromMaven>false</yarnInheritsProxyConfigFromMaven>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>

Also update package.json scripts to run Yarn (yarn run build, etc.). After both changes, the builds aligned and the white screen disappeared. Root cause: mismatch between dev and CI dependency versions.

Notes

What lock files do

lock literally means lock. package-lock.json (npm) and yarn.lock (Yarn) freeze exact versions. Even if package.json has version ranges, lock files make installs deterministic.

From the Yarn docs:

yarn install
Install all the dependencies listed within package.json in the local node_modules folder.

The yarn.lock file is utilized as follows:

If yarn.lock is present and is enough to satisfy all the dependencies listed in package.json, the exact versions recorded in yarn.lock are installed, and yarn.lock will be unchanged. Yarn will not check for newer versions.
If yarn.lock is absent, or is not enough to satisfy all the dependencies listed in package.json (for example, if you manually add a dependency to package.json), Yarn looks for the newest versions available that satisfy the constraints in package.json. The results are written to yarn.lock.

Yarn vs. npm

Both are package managers. Yarn arrived later, tends to be faster, and its CLI feels more semantic. Choose whichever your team standardizes on.

frontend-maven-plugin?

Maven is pervasive in Java projects. frontend-maven-plugin lets us bundle a Node environment with the build, keeping it isolated from the system installation and making deployments portable. See the plugin docs for more usage details.

Summary

Every incident is a learning opportunity—this one reinforced why consistent dependency management matters.

References

Authors
Developer, digital product enthusiast, tinkerer, sharer, open source lover