📖 Scenario: You are building a simple web page using Remix Framework. The page should show a list of products with their prices. You want to start with a basic HTML list that works without JavaScript, then enhance it by adding a filter input that lets users filter products by name using JavaScript.
🎯 Goal: Create a Remix component that first renders a static list of products. Then add a filter input that progressively enhances the page by filtering the list on the client side without breaking the basic HTML functionality.
📋 What You'll Learn
Create a products array with exact product names and prices
Add a filter state variable to hold the user's input
Use a filter function to show only products matching the filter text
Add an input element with proper accessibility attributes for filtering
💡 Why This Matters
🌍 Real World
Progressive enhancement ensures your web pages work for everyone, even if JavaScript is disabled or slow. This approach improves accessibility and user experience.
💼 Career
Understanding progressive enhancement and React state management is essential for frontend developers building resilient and accessible web applications with Remix or similar frameworks.
Progress0 / 4 steps
1
Create the products data array
Create a constant array called products with these exact objects: { name: 'Apple', price: 1 }, { name: 'Banana', price: 0.5 }, and { name: 'Cherry', price: 2 }.
Remix
Hint
Use const products = [ ... ] with objects inside.
2
Add a filter state variable
Add a React state variable called filter initialized to an empty string using useState.
Remix
Hint
Import useState and create const [filter, setFilter] = useState('').
3
Filter the products list based on filter state
Create a constant called filteredProducts that filters products to include only those where product.name.toLowerCase() includes filter.toLowerCase().
Remix
Hint
Use products.filter(product => product.name.toLowerCase().includes(filter.toLowerCase())).
4
Add an accessible filter input and render filtered list
Add an <input> element with aria-label="Filter products", value={filter}, and onChange={e => setFilter(e.target.value)}. Then render filteredProducts as a list of <li> showing product.name and product.price inside a <ul>.
Remix
Hint
Use an input with aria-label, bind value and onChange. Render filteredProducts inside a ul with li items.
Practice
(1/5)
1. What is the main goal of progressive enhancement in Remix apps?
easy
A. Make the app work with basic features first, then add extras
B. Only support users with JavaScript enabled
C. Load all data on the client side after page load
D. Use complex JavaScript frameworks for all features
Progressive enhancement means starting with a simple, working version of the app that works without JavaScript or advanced features.
Step 2: Compare options to this concept
Make the app work with basic features first, then add extras matches this idea by focusing on basic features first, then adding extras. Other options rely on JavaScript or complex frameworks only.
Final Answer:
Make the app work with basic features first, then add extras -> Option A
Quick Check:
Progressive enhancement = basic first, then extras [OK]
Hint: Remember: start simple, add features later [OK]
Common Mistakes:
Thinking progressive enhancement means only using JavaScript
Believing all data must load on client side
Confusing with single-page app behavior
2. Which of the following is the correct way to provide fallback content for users without JavaScript in Remix?
easy
A.
Enable JavaScript.
B.
C. Enable JavaScript to continue.
D. Your browser needs JavaScript enabled.
Solution
Step 1: Identify HTML tag for no-JavaScript fallback
The <noscript> tag is the standard HTML way to show content only when JavaScript is disabled.
Step 2: Check other options for correctness
Options B, C, and D use invalid or non-standard tags or scripts that won't work as fallback content.
Final Answer:
<noscript>Your browser needs JavaScript enabled.</noscript> -> Option D
Quick Check:
Fallback for no JS = <noscript> [OK]
Hint: Use <noscript> tag for no-JS fallback content [OK]
Common Mistakes:
Using custom or invalid tags for fallback
Trying to detect JS with scripts inside fallback
Not providing any fallback content
3. Given this Remix loader code, what will the user see if JavaScript is disabled?
export async function loader() {
return { message: 'Hello from server' };
}
export default function Greeting() {
const data = useLoaderData();
return <div>{data.message}</div>;
}
medium
A. The page reloads infinitely
B. The message 'Hello from server' is shown
C. An error message appears
D. Nothing is shown because JavaScript is disabled
Solution
Step 1: Understand Remix loader and server rendering
The loader runs on the server and sends data to the component before rendering the page.
Step 2: Consider no JavaScript scenario
Since Remix renders on the server, the user sees the server-rendered HTML with the message even if JavaScript is off.
Final Answer:
The message 'Hello from server' is shown -> Option B
Quick Check:
Server data shows without JS = message visible [OK]
Hint: Loader data renders server-side, visible without JS [OK]
Common Mistakes:
Assuming no output without JavaScript
Confusing client-side hooks with server data
Thinking loader runs only on client
4. What is wrong with this Remix component if the goal is progressive enhancement?
A. It does not fetch data on the server, so no content without JavaScript
B. The useEffect hook runs on the server causing errors
C. The fetch URL is incorrect for Remix loaders
D. The component returns invalid JSX
Solution
Step 1: Analyze data fetching method
The component fetches data only inside useEffect, which runs only in the browser after load.
Step 2: Check progressive enhancement impact
Without JavaScript, useEffect never runs, so no data is fetched or shown, breaking progressive enhancement.
Final Answer:
It does not fetch data on the server, so no content without JavaScript -> Option A
Quick Check:
Client-only fetch breaks no-JS support [OK]
Hint: Fetch data in loaders, not useEffect, for no-JS support [OK]
Common Mistakes:
Thinking useEffect runs on server
Ignoring no-JS users
Assuming fetch URL alone fixes enhancement
5. You want to build a Remix page that shows a list of items fetched from the server and also allows client-side filtering with JavaScript. Which approach best follows progressive enhancement?
hard
A. Render static list in HTML and fetch items on client to replace it
B. Fetch items only in useEffect and render empty list server-side
C. Fetch items in the loader and render them server-side; add client filtering with useState and input events
D. Use only client-side rendering with React and no server loader
Solution
Step 1: Identify server-side data fetching
Fetching items in the loader ensures the list is rendered on the server and visible without JavaScript.
Step 2: Add client-side filtering
Using useState and input events for filtering adds extra features for users with JavaScript, enhancing the experience.
Step 3: Evaluate other options
The other options fail to show data server-side or rely only on client JS, breaking progressive enhancement.
Final Answer:
Fetch items in the loader and render them server-side; add client filtering with useState and input events -> Option C
Quick Check:
Server fetch + client filter = progressive enhancement [OK]
Hint: Load data server-side, enhance with client JS features [OK]
Common Mistakes:
Fetching data only on client side
Not rendering initial data server-side
Ignoring progressive enhancement principles
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.