Skip to Content
Course content

56: Scala.js for Frontend Development

Click on the "Edit" button in the top corner of the screen to edit your slide content.

When I first started chatting with developers about Scala.js, the most common thing I heard was: "Why bother? It's just a wrapper around JavaScript. You're basically writing JS with a Scala 'skin,' and you'll spend all your time fighting the compiler to make it behave like the browser wants."

That is fundamentally wrong. If Scala.js were just a skin, it would be a toy. In reality, Scala.js is a full-blown compiler that targets JavaScript, meaning you get the entire power of the Scala type system—generics, implicits, and high-order functions—running natively in the browser. You aren't "simulating" Scala; you are executing it. The magic isn't in how it wraps JS, but in how it allows you to define rigorous boundaries between your typed Scala logic and the untyped chaos of the DOM.

The Myth of the "JS-in-Scala" Wrapper

People think that to use a JavaScript library, you have to write clunky, boilerplate-heavy wrappers that feel like you're writing Java in 2005. They assume you lose the "Scala feel" the moment you touch the frontend. But let's look at how we actually handle an external JS object. Suppose you have a tiny JS utility in your project that handles user session data:

// session-util.js
export const sessionManager = {
  saveSession: (userId, token) => {
    localStorage.setItem('user_id', userId);
    localStorage.setItem('auth_token', token);
  },
  getSession: () => ({
    id: localStorage.getItem('user_id'),
    token: localStorage.getItem('auth_token')
  })
};

If this were just a "wrapper," you'd be casting things to Any and praying you didn't misspell a key. Instead, you define a facade. You tell Scala exactly what the JS object looks like, and from that point on, the compiler protects you.

Bridging the Gap with Type-Safe Interop

To interact with the code above, we don't "wrap" it in the traditional sense. We describe its interface. I prefer using js.Object and @JSImport to create a clean bridge. Here is how I would actually implement the interface for that session manager:

import scala.scalajs.js
import scala.scalajs.js.annotation._

@JSImport("./session-util.js", "sessionManager")
object SessionManager extends js.Object {
  def saveSession(userId: String, token: String): js.Unit = js.native
  def getSession(): SessionData = js.native
}

@js.native
trait SessionData extends js.Object {
  val id: String
  val token: String
}

Notice the js.native keyword. I'm not writing the implementation here; I'm telling the Scala compiler: "Trust me, this method exists in the JavaScript runtime, and it follows this signature." Now, in the rest of your application, you can call SessionManager.saveSession("123", "abc") with full type safety. If you try to pass an Integer where a String is expected, your code won't even compile. You've successfully brought the volatility of JavaScript under the control of the Scala type system.

I should mention that you aren't limited to just calling JS functions. You can use Scala's powerful collections and functional paradigms to manipulate data on the frontend, then pass the final result to the DOM. The real power of Scala.js is when you share code. If you have a case class User(id: String, email: String) in a shared module, you can use that exact same class in your backend (JVM) and your frontend (JS). No more duplicating DTOs or manually parsing JSON on both ends just to make sure the fields match.




📋 Practical Task

Implement a LocalStorage User Preference Store

Your task is to create a type-safe Scala.js interface for a browser-based preference manager. You need to bridge a JavaScript object that handles theme and language settings.

The JavaScript environment provides this object:

const prefStore = {
  setPreference: (key, value) => {
    localStorage.setItem(`pref_${key}`, value);
  },
  getPreference: (key) => {
    return localStorage.getItem(`pref_${key}`);
  }
};

Requirements:

  • Create a @JSImport object named PrefStore that maps to the prefStore JS object.
  • Define the setPreference and getPreference methods using js.native.
  • Ensure the methods use String for both keys and values.
  • Write a small Scala function updateTheme(newTheme: String): Unit that uses your PrefStore facade to save a theme preference under the key "theme".
Rating
0 0

There are no comments for now.

to be the first to leave a comment.