Browser Reactivity
Adding client-side interactivity
Concepts
- Client-side code runs in the browser after the page loads,
without contacting the server
- Changes are instant but cannot be trusted for security
- Why can't such changes be trusted for security decisions like access control?
- Declarative code says what the outcome should be;
imperative code says how to achieve it
- Alpine.js is declarative: show this element when
openis true - When is declarative simpler than imperative and vice versa?
- Alpine.js is declarative: show this element when
- Alpine.js adds interactivity through HTML attributes,
so the behavior lives in the template rather than in a separate JavaScript file
- No build step is needed
- What practical problem does keeping behavior in the HTML template rather than a separate JavaScript file solve for a small research application?
x-datadefines a reactive state object attached to an elementx-showhides or reveals the element based on a state value@clickruns an expression when the element is clicked- Why do some of these attributes' names start with 'x-' while others start with '@'?
- When a reactive state variable changes,
Alpine.js automatically updates every element that depends on it
- You describe what the UI should look like in each state rather than writing the steps to get there
- What would you write in plain JavaScript to achieve the same show/hide behavior as
x-show?
- A client-side guard (like an "are you sure?" dialog) improves user experience
but cannot prevent a determined user from bypassing it
- Destructive server actions must always be protected by server-side checks
- If you set
disabledon the delete button using JavaScript after a failed confirmation, can a user still send the delete request to the server?
What Alpine.js is
- A lightweight JavaScript library for small pieces of interactivity:
show/hide, toggle, simple state
- Describe three other things Alpine.js can do
- Not a replacement for frameworks like React
- Describe something that would be too complex for Alpine.js
Including Alpine.js
- Loading from a CDN via a script tag
- Add the Alpine.js CDN script tag to the page template
- Why this is fine for a research tool and when it would not be acceptable
- Explain two situations where loading a JavaScript library from a CDN would be unacceptable
Showing and hiding a details panel
x-data,x-show, and@click- Use Alpine.js to highlight a table row in a different background color when it is selected, and clear the highlight when a different row is selected
- Expanding a row to show extra fields without a page reload
- Update the extra-details panel to display the measurement ID, survey date, and observer name for that row
A confirmation dialog
- Using Alpine.js to ask "are you sure?" before a delete action
- Add an Alpine.js confirmation dialog that appears when a delete button is clicked and only proceeds if the user confirms
- Client-side guards versus server-side guards
- Explain why the server-side delete route must also check authorization even though a confirmation dialog already exists on the client
Reviewing the generated JavaScript
- What the LLM wrote, what Alpine.js syntax looks like
- Introduce a deliberate bug into the Alpine.js code that was just generated, then identify and fix it
- The difference between declarative and imperative code
- Rewrite the confirmation dialog using plain JavaScript and compare the two approaches
Check for Understanding
What is the difference between client-side and server-side code, and which one runs after the page loads without a network request?
Client-side code (JavaScript in the browser) runs on the user's machine after the page has loaded; it can modify what the user sees instantly without contacting the server. Server-side code runs on the server and produces a response; the browser must make an HTTP request to trigger it. Client-side code runs without a network request; server-side code always requires one.
What does x-data="{ open: false }" do, and how does x-show="open" use it?
x-data attaches a reactive state object { open: false } to the element and all its children.
x-show="open" evaluates the expression open against that state
and hides the element (sets display: none) when it is falsy.
When open is set to true (e.g., by clicking a button with @click="open = true"),
Alpine.js removes the hidden style and the element becomes visible—without any page reload.
Why is a client-side "are you sure?" dialog not a sufficient security check for a destructive action?
The dialog runs JavaScript in the browser,
which the user controls.
A technically sophisticated user can open the browser console,
call the delete endpoint directly with fetch(),
or craft an HTTP request with curl,
bypassing the dialog entirely.
Security checks must happen on the server,
where they cannot be bypassed by the client.
What does "declarative" mean in the context of Alpine.js, and how does it differ from writing document.getElementById(...).style.display = 'none'?
Declarative code describes the desired state:
"this element is visible when open is true."
Alpine.js works out how to make that true.
Imperative code (getElementById followed by style.display = 'none') describes the steps:
find the element and then change a property.
Declarative code is usually easier to read because it says what, not how,
and the framework keeps the UI consistent with the state automatically.
Exercises
Add a collapsible section
Use Alpine.js to add a toggle button to each table row that shows and hides a "technical details" panel below the row containing fields like measurement ID, data entry date, and observer name. The panel should be hidden by default. Prompt the LLM to write the Alpine.js markup, then read each attribute and explain what it does.
Live character count
Add a notes field to the data-entry form and use Alpine.js to display a live character count below the input that updates as the user types. Show the count as "12/200 characters" where 200 is the maximum allowed length. Implement it yourself first, then compare to what the LLM produces.
Sort without a reload
Prompt the LLM to use Alpine.js to sort the visible table rows by clicking a column header without making a server request. What are the limitations of sorting data already in the browser versus sorting in SQL?
Audit the generated JavaScript
Prompt the LLM to explain, attribute by attribute, every piece of Alpine.js syntax it generated for this lesson. Write a one-sentence correction for any explanation you disagree with.