Getting Started with Scala.js and Vite
In this first tutorial, we learn how to get started with Scala.js and Vite. We use Vite to provide live-reloading of the Scala.js application in the browser for development. We also configure it to build a minimal bundle for production.
Going through this tutorial will make sure you understand the basic building blocks. If you prefer to skip this step and directly write Scala.js code, you may jump to Getting Started with Scala.js and Laminar.
If you prefer to look at the end result for this tutorial directly, checkout the scalajs-vite-end-state branch instead of creating everything from scratch.
Prerequisites
Make sure to install the prerequisites before continuing further.
Vite template
We bootstrap our setup using the vanilla Vite template. Navigate to a directory where you store projects, and run the command
Choose a project name (we choose livechart
).
Select the “Vanilla” framework and the “JavaScript” variant.
Our output gives:
As instructed, we follow up with
Open the provided URL to see the running JavaScript-based hello world.
Exploring the template
In the generated folder, we find the following relevant files:
index.html
: the main web page; it contains a<script type=module src="/main.js">
referencing the main JavaScript entry point.main.js
: the main JavaScript entry point; it sets up some DOM elements, and sets up a counter for a button.counter.js
: it implements a counter functionality for a button.package.json
: the config file fornpm
, the JavaScript package manager and build orchestrator.
Remarkably, there is no vite.config.js
file, which would be the configuration for Vite itself.
Vite gives a decent experience out of the box, without any configuration.
Live changes
One of the main selling points of Vite is its ability to automatically refresh the browser upon file changes.
Open the file main.js
and change the content of the <h1>
element:
Observe that the page automatically and instantaneously refreshes to show the changes.
Introducing Scala.js
We use sbt as a build tool for Scala and Scala.js. We set it up as follows.
In the subdirectory livechart/project/
, we add two files: build.properties
and plugins.sbt
.
project/build.properties
: set the version of sbt
project/plugins.sbt
: declare sbt plugins; in this case, only sbt-scalajs
At the root of our livechart/
project, we add one file: build.sbt
.
build.sbt
: the main sbt build
Finally, we write the following content in the file src/main/scala/livechart/LiveChart.scala
:
Note that the above is not idiomatic Scala, but rather a direct translation of the Vite template code into Scala.js. We will see in the next tutorial how to use Laminar to write it more idiomatically.
For the most part, the Scala.js version uses straightforward Scala syntax corresponding to the original JavaScript code.
The definition of javascriptLogo
deserves some explanation.
We translated it from the JavaScript import
which is actually a shorthand for
Many bundlers, Vite included, treat import
s with asset files such as .svg
as pseudo-modules whose default
import is the file path to the corresponding asset in the processed bundle.
Further down, we use it as the value for the src
attribute an <img>
tag.
Read more about this mechanism in the Vite documentation on static asset handling.
The translation in Scala.js reads as
The @js.native
annotation tells Scala.js that javascriptLogo
is provided externally by JavaScript.
The @JSImport("/javascript.svg", JSImport.Default)
is the translation of the default
import from the /javascript.svg
pseudo-module.
Since it represents a file path, we declare javascriptLogo
as a String
.
The = js.native
is a Scala.js idiosyncrasy: we need a concrete value to satisfy the Scala typechecker.
In an ideal world, it would not be required.
We can now build the Scala.js project by opening a new console, and entering sbt:
The fastLinkJS
task produces the .js
outputs from the Scala.js command.
The ~
prefix instructs sbt to re-run that task every time a source file changes.
There is one thing left to change: replace the hand-written JavaScript code with our Scala.js application.
We use the @scala-js/vite-plugin-scalajs
plugin to link Vite and Scala.js with minimal configuration.
We install it in the dev-dependencies with:
and instruct Vite to use it with the following configuration in a new file vite.config.js
:
Finally, open the file main.js
, remove almost everything to leave only the following two lines:
When we import
a URI starting with scalajs:
, vite-plugin-scalajs
resolves it to point to the output directory of Scala.js’ fastLinkJS
task.
You may have to stop and restart the npm run dev
process, so that Vite picks up the newly created configuration file.
Vite will refresh the browser with our updated “Hello Scala.js!” message.
Live changes with Scala.js
Earlier, we noticed how changing the JavaScript files caused Vite to immediately refresh the browser. Is that also the case if we change the Scala source files?
Indeed it is. Let us change the message to
Once we save, we notice that the browser refreshes with the updated message.
There are two things happening behind the scenes:
- The
~fastLinkJS
task in sbt notices that a.scala
file has changed, and therefore rebuilds the.js
output. - The
npm run dev
process with Vite notices that a.js
file imported from/main.js
has changed, and triggers a refresh with the updated files.
All these steps are incremental.
When we change a single Scala file, only that one gets recompiled by the Scala incremental compiler.
Then, only the affected small .js
modules produced by fastLinkJS
are regenerated.
Finally, Vite only reloads those small .js
files that were touched.
This ensures that the development cycle remains as short as possible.
Production build
The fastLinkJS
task of sbt and the npm run dev
task of Vite are optimized for incremental development.
For production, we want to perform more optimizations on the Scala.js side and bundle minimized files with npm run build
.
We stop Vite with Ctrl+C
and launch the following instead:
Since the built website uses an ECMAScript module, we need to serve it through an HTTP server to visualize it.
We can use Vite’s preview
mode for that purpose, as we can run it without any additional dependency:
Navigate to the mentioned URL to see your website.
Conclusion
In this tutorial, we saw how to configure Scala.js with Vite from the ground up using @scala-js/vite-plugin-scalajs
.
We used sbt as our build tool, but the same effect can be achieved with any other Scala build tool, such as Mill or scala-cli.
Our setup features the following properties:
- Development mode with live reloading: changing Scala source files automatically triggers recompilation and browser refresh.
- Production mode taking the fully optimized output of Scala.js and producing a unique
.js
file.
In our next tutorial about Laminar, we will learn how to write UIs in idiomatic Scala code.