log
A type of live region where new information is added in meaningful order and old information may disappear. Perfect for chat applications, activity feeds, and sequential data streams.
Overview
The log role is used for sequential information where new items are added in a meaningful order. Unlike other live regions, logs typically only announce new additions, not the entire history.
With aria-atomic="false" by default, screen readers announce only the newly added content, making it ideal for chat applications, activity feeds, and system logs.
Live Demo: Activity Log
Note: Only new entries are announced to screen readers, not the entire log. This prevents overwhelming users with redundant information.
Code Examples
Basic Log
<!-- Basic Log (Chat/Activity Feed) -->
<div role="log" aria-label="Chat Messages">
<div class="message">Alice: Hello!</div>
<div class="message">Bob: Hi there!</div>
<!-- New messages will be announced as they're added -->
</div>
<!-- Implicit aria-live="polite" and aria-atomic="false" -->Chat Application
<!-- Chat Application -->
<div id="chat-log"
role="log"
aria-label="Chat conversation"
aria-atomic="false">
<!-- Messages are added here -->
</div>
<script>
function addChatMessage(user, text) {
const log = document.getElementById('chat-log');
const message = document.createElement('div');
message.className = 'chat-message';
message.innerHTML = `
<strong>${user}:</strong>
<span>${text}</span>
<time>${new Date().toLocaleTimeString()}</time>
`;
log.appendChild(message);
// Screen reader announces: "Alice: Hello everyone!"
// (only the new message, not the entire chat history)
// Auto-scroll to latest
log.scrollTop = log.scrollHeight;
}
</script>Activity Log
<!-- Activity/Audit Log -->
<div id="activity-log"
role="log"
aria-label="Recent Activity"
class="log-container">
<div class="log-entry">
<time>10:30 AM</time>
<span>User login successful</span>
</div>
<div class="log-entry">
<time>10:31 AM</time>
<span>File uploaded: report.pdf</span>
</div>
</div>
<script>
function logActivity(action) {
const log = document.getElementById('activity-log');
const entry = document.createElement('div');
entry.className = 'log-entry';
entry.innerHTML = `
<time>${new Date().toLocaleTimeString()}</time>
<span>${action}</span>
`;
log.appendChild(entry);
// Keep only last 50 entries
while (log.children.length > 50) {
log.removeChild(log.firstChild);
}
}
</script>React Component
// React Log Component
import { useState, useRef, useEffect } from 'react';
function ChatLog({ messages }) {
const logRef = useRef(null);
// Auto-scroll to bottom when new message
useEffect(() => {
if (logRef.current) {
logRef.current.scrollTop = logRef.current.scrollHeight;
}
}, [messages]);
return (
<div
ref={logRef}
role="log"
aria-label="Chat Messages"
aria-atomic="false"
className="chat-log"
>
{messages.map((msg, index) => (
<div key={index} className="message">
<strong>{msg.user}:</strong>
<span>{msg.text}</span>
<time>{msg.timestamp}</time>
</div>
))}
</div>
);
}
// Activity Log Example
function ActivityLog() {
const [activities, setActivities] = useState([]);
const addActivity = (action) => {
const newActivity = {
id: Date.now(),
action,
timestamp: new Date().toISOString(),
};
setActivities(prev => {
// Keep only last 100 entries
const updated = [...prev, newActivity];
return updated.slice(-100);
});
};
return (
<div
role="log"
aria-label="Activity Feed"
className="activity-log"
>
{activities.map(activity => (
<div key={activity.id} className="activity">
<time>{new Date(activity.timestamp).toLocaleTimeString()}</time>
<span>{activity.action}</span>
</div>
))}
</div>
);
}Best Practices
Use for chat messages and conversations
Use for activity feeds and history logs
Use for console output and debug messages
Provide aria-label to identify the log
Limit log size - remove old entries to improve performance
Auto-scroll to show newest entries
Don't use for critical alerts - use role="alert" instead
Don't let logs grow infinitely - implement cleanup
Don't use for static, unchanging lists