I recently started a new Scala project that uses the Scala/Scala.js Laminar library for frontend development (i.e., as a JavaScript replacement).
Laminar is a library that’s built on top of Scala.js to let you build “reactive” web applications with observables, and in this tutorial I’ll show how to create a static “Hello, world” application from scratch. Once we get past these basics, I’ll show in Part 2 of this series how to create a reactive single-page application (SPA) with Laminar.
If you’re interested in the source code for this example, you can find it as a little Github project here:
So let’s get started building our static “Hello, world” app!
What you need
For this “Hello, world” tutorial you’ll need to create these files within an sbt project:
- project/plugins.sbt
- build.sbt
- Laminar101.scala
- index.html
Once those are in place you’ll compile the Laminar application with sbt and Scala.js, and then view it in your browser.
project/plugins.sbt
First, add this code to a project/plugins.sbt file:
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.10.0")
This is just some boilderplate stuff you need to use Scala.js in an sbt project.
build.sbt
Next, create a build.sbt file with these contents:
// enable the Scala.js plugin that’s in 'project/plugins.sbt'
enablePlugins(ScalaJSPlugin)
// this states that this project has a Scala.js application with a `main` method
scalaJSUseMainModuleInitializer := true
// this specifies the name of the `main` method
Compile/mainClass := Some("alvin.Laminar101")
// these are basic settings that you’ll see in every sbt project.
// they specify the project name, version, and version of Scala you want
// to use.
lazy val root = project
.in(file("."))
.settings(
name := "Laminar101",
version := "0.1",
scalaVersion := "2.13.8"
)
// this specifies that Laminar is a dependency for this project.
// note that Laminar depends on Scala.js and Airstream, but we don’t need
// to specify those versions here, because they are dependencies of Laminar.
libraryDependencies ++= Seq(
"com.raquo" %%% "laminar" % "0.14.2"
)
Laminar101.scala
Next, create this file named Laminar101.scala in the src/main/scala directory:
package alvin
// you’ll use these two import statements all the time.
// notice that the 'org.scalajs.dom' project is also used by Laminar.
import com.raquo.laminar.api.L._
import org.scalajs.dom
// the name 'Laminar101' matches the 'main' method setting in the
// build.sbt file (along with the package name 'alvin').
object Laminar101 {
// the 'main' method
def main(args: Array[String]): Unit = {
// create a <div> that contains an <h1> tag. these methods come from
// the 'com.raquo.laminar.api.L._' import.
val rootElement: HtmlElement = div(
h1("Hello, world")
)
// `#root` here must match the `id` in index.html
val containerNode = dom.document.querySelector("#root")
// this is how you render the rootElement in the browser
render(containerNode, rootElement)
}
}
This is basically the simplest-possible Laminar “Hello, world” example I can create. It doesn’t contain any dynamic parts; instead, it just displays an <h1>
tag with the “Hello, world” text in it. The comments
An important thing to note is that the #root
selector in this code must match the id
in the index.html file that follows.
index.html
The final step is to create this index.html file in the src/main/resources directory:
<!DOCTYPE html>
<html>
<head>
<title>Hello Laminar World</title>
<link rel="stylesheet" href="./app.css">
</head>
<body>
<div id="root"></div>
<script type="text/javascript"
src="../../../target/scala-2.13/laminar101-fastopt.js"></script>
</body>
</html>
When you look at the Girhub project you’ll see that there is also an app.css file in the src/main/resources directory, but it isn’t important for our purposes.
The important thing here is this code:
<script type="text/javascript"
src="../../../target/scala-2.13/laminar101-fastopt.js"></script>
The laminar101-fastopt.js file will be created in the step, and it’s generated from our Laminar101.scala source code. (Technically, the Scala code is transpiled (or compiled) into JavaScript code.)
Run your Laminar application
With that setup we’re ready to run our project. Start sbt
in root directory of the project, and once it starts up, run this ~fastOptJS
command at the sbt prompt:
sbt:Laminar101> ~fastOptJS
This tells sbt to compile our Laminar101.scala code into the laminar101-fastopt.js JavaScript file.
When you run that command you should see sbt output that looks like this:
sbt:Laminar101> ~fastOptJS
[info] compiling 1 Scala source to /Users/al/Projects/Scala/Laminar/Tutorials/Laminar101/target/scala-2.13/classes ...
[info] Fast optimizing /Users/al/Projects/Scala/Laminar/Tutorials/Laminar101/target/scala-2.13/laminar101-fastopt
[success] Total time: 4 s, completed Jun 18, 2022, 12:25:30 PM
[info] 1. Monitoring source files for root/fastOptJS...
[info] Press <enter> to interrupt or '?' for more options.
Finally, open a new terminal window and cd
to the src/main/resources directory, and open the index.html file. On macOS you do that with the open
command:
src/main/resources> open index.html
When you do that, you should see the “Hello, world” text rendered in your browser.
If you’re not using a Mac, you can open that file in other ways, including using a URL in your browser that will look something like this:
file:///Users/alvin/Projects/Scala//Laminar101/src/main/resources/index.html
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
Discussion
If you want to experiment with this code a little bit, try adding some other HTML tags in the Laminar101.scala file, like this:
div(
h1("Hello, world"),
p("It’s me!"),
p("Yo!")
)
As you’ll see, this is very similar to writing HTML code.
This article shows everything you need to get started with Laminar. In the next tutorial in this series I show how to use Laminar’s “reactive” features to develop single-page web applications.