What is Declarative UI?

Jetpack Compose is the latest Android UI development framework from Google that nowadays piques my interest. Jumping from using Java to Kotlin was for the most part an enjoyable experience: I feel I can be expressive and more efficient with Kotlin. I get that same feeling whenever I’m digging further into Jetpack Compose, compared to the usual method of using XML files for UI development (also known as the imperative paradigm).

I haven’t actually used Jetpack Compose in production, and in this post I’d like to get to know it better by nerding out on its paradigm, declarative UI.

What is Jetpack Compose? From the announcement post:

Jetpack Compose is a fully declarative component-based approach, meaning you describe your UI as functions that transform data into a UI hierarchy. When the underlying data changes, the Compose framework automatically updates the UI hierarchy for you, making it simple to build UIs easily and quickly.

(emphasis mine)

This matches this cool formula I found on Flutter’s “Start thinking declaratively” documentation:

A mathematical formula: UI = f(state). 'UI' is the layout on the screen. 'f' is your build methods. 'state' is the application state.

What’s the difference between declarative and imperative? Let’s start with digging linguistically. These are definitions from

declare [ dih-klair ]
to make known or state clearly, especially in explicit or formal terms

imperative [ im-per-uh-tiv ]
of the nature of or expressing a command; commanding.

If we bring these definitions back to UI development, what does it mean to declare/state a user interface, compared to commanding a user interface?

I found this illuminating example from a PDF lecture from MIT about Declarative UI. Declarative UI can be found within this HTML example:

<div id=“main”>
<div id=“toolbar”>
<img src=“cut.png”></img>
<textarea id=“editor”></textarea>

And here’s how a relatively similar UI can be done imperatively in Java:

JPanel main = new JPanel();
JPanel toolbar = new JPanel();
JButton button = new JButton();
JTextArea editor = new JTextArea();

The way I understand it, the HTML is about describing the UI I want to display. It’s the what. Conversely, the Java example has commands in it (create new toolbar, create new button, set the button label, and so on). It’s the how. The difference between what and how seems to be the simplest way to explain the declarative vs imperative paradigm.

Declarative paradigm used on UI development are pretty popular, lately. Prominent examples are:

  • ReactJS in JavaScipt development
  • SwiftUI in iOS
  • Jetpack Compose in Android
  • Flutter for multi-platform development

It is often described that using the declarative paradigm, the key idea is to think about how a UI component should look like under any possible states. For example: how should an input box looks like if it’s empty? What if the user entered invalid information?

When writing React, it’s often good not to think of how you want to accomplish a result, but instead what the component should look like in its new state.

“Declarative vs Imperative Programming”

Coming back to the formula above, it means that essentially we’ll be writing component builder functions where we declare how the component should look like, given some parameters for the state.

From Jonathan Romano:

Benefits you get from declarative programming are largely in making code read more naturally. It allows for your code to visually convey structure and meaning. It allows for the entirety of the intent of some code to be described in one place. It allows a developer to spend less time parsing and instead jump right to the answer of “what does this code do?” It can also mean abstracting away the details of complex APIs behind a cleaner interface, letting someone else deal with the complexity of making sure everything is executed properly.

With Android development, the common, imperative way is to use XML layout files to declare the UI components, then we mutate and manipulate them inside the Java/Kotlin activity/fragment/others classes. The structure and meaning are separated. They’re okay once I’m used to it, however the declarative paradigm indeed seems like an improvement.

Here’s an example code from Jetpack Compose’s tutorial:

fun NewsStory() {
    val image = imageResource(R.drawable.header)
        modifier = Modifier.padding(16.dp)
    ) {
        val imageModifier = Modifier
            .clip(shape = RoundedCornerShape(4.dp))

        Image(image, modifier = imageModifier,
                  contentScale = ContentScale.Crop)

        Text("A day in Shark Fin Cove")
        Text("Davenport, California")
        Text("December 2018")

It’s for creating a “news story” component with an image at the top, then some text below it. The fun part is, that is all that’s needed. There’s no XML layout file anymore, and to me they’re so much easier to read.

Of note, see how the “news story” component above is set as a function: fun NewsStory(), which reminds me again to Flutter’s formula UI = f(state). This encourages creations of custom UI components that can be reused whenever needed.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s