Implementing a RecyclerView in Android often involves writing a significant amount of boilerplate code, especially when dealing with complex or dynamic lists. This repetitive setup can slow down development and increase errors. To fix this, Airbnb introduced the Epoxy library, which simplifies RecyclerView implementation through a more declarative and composable approach.
Epoxy lets developers create models that represent each item in a RecyclerView. These models include both the data and how the item should be laid out. Instead of writing and managing the RecyclerView adapter manually, you just define what you want to display, and Epoxy handles the rest. It automatically takes care of creating view holders, binding data to views, and calculating the differences between old and new data. This results in cleaner, more maintainable code with less boilerplate and fewer bugs.

Get Started with Epoxy
To get started with the Epoxy library, we need to add a few dependencies. First, navigate to Gradle Scripts > build.gradle.kts (Module :app) and add the following dependencies under the dependencies{} scope.
dependencies {
...
implementation ("com.airbnb.android:epoxy:5.1.4")
implementation ("com.airbnb.android:epoxy-databinding:5.1.4")
implementation ("com.airbnb.android:epoxy-annotations:5.1.4")
kapt ("com.airbnb.android:epoxy-processor:5.1.4")
}
Check for the updated version of the epoxy library from here.
Now, add the kapt plugin (for Kotlin users only) under the plugins{} scope in the same file.
plugins {
...
id ("kotlin-kapt")
}
Finally, add the following line of code in the same file
kapt {
correctErrorTypes = true
}
Components of Epoxy
Epoxy consists of mainly two components:
- EpoxyModels
- EpoxyController
EpoxyModels
EpoxyModels represent a single item (view) in the Epoxy Recycler View. It is used by Epoxy to determine what views to be displayed and how to bind them with data. These models are immutable and are responsible to bind data to their respective views. There are mainly 2 types of Models:
- Generated Models: Epoxy can generate models from custom views (using @ModelView), data binding layouts, or traditional ViewHolder patterns.
- Manual Models: You can also define your own models by extending EpoxyModel or its subclasses.
EpoxyController
EpoxyController acts as a bridge between your data and the models (in this case EpoxyModels). It is responsible for generating a list of EpoxyModel instances, which represent the individual items or views to be displayed in the RecyclerView. It listens for data changes (manually or via requestModelBuild()), and automatically rebuilds the model list based on the latest data. Similar to MVVM or MVI patterns, it separates the data/state layer from the UI rendering logic, promoting a clean architecture.
Step by Step Implementation
Step 1: Create a new project
To create a new project in the Android Studio, please refer to How to Create/Start a New Project in Android Studio?
Note: Select Kotlin as the programming language, since this demonstration is shown in Kotlin.
Step 2: Add dependencies for Epoxy Library
Refer to the Get Started with Epoxy section of this article, to know how to add the dependencies
Step 3: Create a Kotlin Helper Model class
Navigate to app > kotlin+java > {package-name}, right click on the folder and select, New > Kotlin Class/File and name the file as KotlinHelperModel.kt. Add the following code to the file.
KotlinHelperModel.kt:
package org.geeksforgeeks.demo
import android.view.View
import androidx.annotation.IdRes
import androidx.annotation.LayoutRes
import com.airbnb.epoxy.EpoxyModel
import kotlin.properties.ReadOnlyProperty
abstract class KotlinHelperModel(
@LayoutRes private val layoutRes: Int
) : EpoxyModel<View>() {
private var view: View? = null
abstract fun bind()
override fun bind(view: View) {
this.view = view
bind()
}
override fun unbind(view: View) {
this.view = null
}
override fun getDefaultLayout() = layoutRes
protected fun <V : View> bind(@IdRes id: Int) =
ReadOnlyProperty<KotlinHelperModel, V> { _, property ->
view?.findViewById(id)
?: throw IllegalStateException("View ID $id for '${property.name}' not found.")
}
}
Step 4: Create an layout file for each model
Navigate to app > res > layout and create a layout with the name item_data_class.xml and add the following code.
item_data_class.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
</LinearLayout>
Design UI:

Step 5: Create a model class
Create a kotlin file with the name Model.kt and add the following code to the file
Model.kt:
package org.geeksforgeeks.demo
import android.widget.TextView
class Model(
private val text: String
) : KotlinHelperModel(R.layout.item_data_class) {
private val tv by bind<TextView>(R.id.tv)
override fun bind() {
tv.text = text
tv.setOnClickListener {
// handle click
}
}
}
Step 6: Create the Controller class
Create a kotlin file with the name Controller.kt and add the following code to the file.
Controller.kt:
package org.geeksforgeeks.demo
import com.airbnb.epoxy.TypedEpoxyController
class Controller() : TypedEpoxyController<List<String>>() {
override fun buildModels(data: List<String>?) {
data?.forEachIndexed { index, str ->
Model(str)
.id(index)
.addTo(this)
}
}
}
Step 7: Working with MainActivity
Navigate to MainActivity.kt and activity_main.xml and make the following changes.
package org.geeksforgeeks.demo
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.airbnb.epoxy.EpoxyRecyclerView
class MainActivity : AppCompatActivity() {
// initialize controller
private val controller by lazy { Controller() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// set up recycler view
val recyclerView = findViewById<EpoxyRecyclerView>(R.id.rv)
// set controller
recyclerView.adapter = controller.adapter
// set data to the controller
controller.setData(List(10) { "Item #$it" })
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<com.airbnb.epoxy.EpoxyRecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Design UI:

Output:
