Optimal Loading Times Contribute to a Better UX
Introduction
Pursuing shorter wait times and faster feedback is crucial. However, rushing to present information to users may not be optimal. For example:
- Entering the webpage
- Feedback: “Loading data…”
- Displaying the loaded data
This process is simple and straightforward; what could be the issue? The problem lies in timing and duration.
Do Not Present Awkward Experiences to Users
Users cannot comprehend that the loading has ended within 30 milliseconds; they only see a flash of information.
Pursuing immediate feedback as much as possible is good, but if the loading information expires quickly due to “immediate feedback,” it becomes an incomprehensible noise experience. Consider:
- The form of the loading experience
- How long users need to understand
- The necessity and significance of the loading experience
User Perception Experience Principles
The concepts of 0.1 / 1 / 10 seconds stem from Response Times: The 3 Important Limits, which divides the definitions of three types of experiences:
- 0.1 seconds: the instant feeling
- 1 second: the feeling of smoothness
- 10 seconds: the limit of patience
Although different groups, times, individuals, and loading experiences have different standards and impacts, relevant concepts can still be derived.
Different Forms of Loading Experiences
To avoid negative experiences caused by waiting, it is necessary to build an appropriate feedback interface. Specifically, common loading interfaces include the following types, each with its applicable scenarios and trade-offs:
- Text Loader: A string of text used to inform the loading status.
- Loader / Spinner: An animated graphic used to inform the loading status.
- Progress Bar: A progress bar used to indicate loading progress.
- UI Skeleton: A low-fidelity flickering interface indicating the loading status.
For instance, a UI skeleton can effectively soften the boundary between loading and results, with extremely low cognitive load and often no need for additional animated transitions to ease the experience, but it may not be suitable for every interface.
Minimum Loading Time Code Practice
Consider three components displaying a fruit list, each with response times of 10 / 100 / 1000 milliseconds:
<FruitList :response-time="10"></FruitList><FruitList :response-time="100"></FruitList><FruitList :response-time="1000"></FruitList>
<template> <div class="fruit-list"> <h2>Fruit List</h2>
<div class="actions"> <button @click="fetchFruits" :disabled="loading"> {{ loading ? 'Loading fruit list…' : 'Get fruit list' }} </button> <button @click="clear" :disabled="loading"> Clear fruit list </button> </div>
<div class="status"> <p v-if="loading">Waiting for response...</p> </div>
<ul v-if="fruits.length && !loading"> <li v-for="(fruit, index) in fruits" :key="index">{{ fruit }}</li> </ul>
<p v-else-if="!loading && !fruits.length">No fruit data available.</p> </div></template>
<script setup lang="ts">import { ref, onMounted } from 'vue'
const { responseTime } = defineProps<{ responseTime: number}>()
type Fruit = string
function getFruits(): Promise<Fruit[]> { return new Promise((resolve, reject) => { setTimeout(() => { resolve(['Apple', 'Banana', 'Mango', 'Kiwi']) }, responseTime) })}
const fruits = ref<Fruit[]>([])const loading = ref(false)
async function fetchFruits() { loading.value = true try { const result = await getFruits() fruits.value = result } catch (err: unknown) { fruits.value = [] } finally { loading.value = false }}
function clear() { fruits.value = []}
onMounted(() => { fetchFruits()})</script>
Obviously a 10-millisecond loading time is extremely challenging for most people to comprehend. By creating a promiseWithMinimumTime
function, results can be returned at a reasonable timing.
- Input: Trigger function with minimum n milliseconds trigger time.
- Calculation:
- If greater than n, execute.
- If less than n, execute at n.
<FruitList :response-time="10" :min-loading-time="100" /><FruitList :response-time="100" :min-loading-time="100" /><FruitList :response-time="1000" :min-loading-time="100" />
const { loading: isLoading, result: fruits, load: loadFruitList } = useLoadWithMinimumTime(getFruits, { minimumTime: minLoadingTime,})
import { ref, type Ref } from 'vue'
function promiseWithMinimumTime<T>(promise: Promise<T>, minimumTime: number): Promise<T> { return new Promise((resolve, reject) => { const startTime = Date.now()
promise .then(result => { const elapsedTime = Date.now() - startTime const remainingTime = minimumTime - elapsedTime
if (remainingTime > 0) { setTimeout(() => { resolve(result) }, remainingTime) } else { resolve(result) } }) .catch(error => { const elapsedTime = Date.now() - startTime const remainingTime = minimumTime - elapsedTime
if (remainingTime > 0) { setTimeout(() => { reject(error) }, remainingTime) } else { reject(error) } }) })}
interface UseLoadWithMinimumTimeOptions { minimumTime?: number}
export function useLoadWithMinimumTime<T>( promiseFn: () => Promise<T>, options: UseLoadWithMinimumTimeOptions = {},) { const { minimumTime = 0 } = options
const loading = ref(false) const error = ref('') const result = ref<T>()
async function load() { loading.value = true error.value = ''
try { const promise = promiseFn() const promiseWithMinTime = promiseWithMinimumTime(promise, minimumTime) const promiseResult = await promiseWithMinTime result.value = promiseResult } catch (err: unknown) { result.value = undefined if (err instanceof Error) { error.value = err.message } else if (typeof err === 'string') { error.value = err } else { error.value = 'An unknown error occurred.' } } finally { loading.value = false } }
return { loading, error, result, load, }}
Conclusion
Every UI should provide feedback at appropriate times, even potentially with animations to aid understanding.
- Most users are already used to these kinds of UI quirks.
- Adding extra logic means more stuff to understand and maintain.
- It might not bring any big visible gains—just some insights or small UX tweaks.
Further Reading
- You Don’t Need Animations - Emil Kowalski
- Should a “loading” text or spinner stay a minimum time on screen?