Chris wrote about “Likes” pages a long while back. The idea is rather simple: “Like” an item in your RSS reader and display it in a feed of other liked items. The little example Chris made is still really good.
There were two things Chris noted at the time. One was that he used a public CORS proxy that he wouldn’t use in a production environment. Good idea to nix that, security and all. The other was that he’d consider using WordPress transients to fetch and cache the data to work around CORS.
I decided to do that! The result is this WordPress block I can drop right in here. I’ll plop it in a <details>
to keep things brief.
Open Starred Feed
Don’t Wrap Figure in a Link
In my post Brief Note on Figure and Figcaption Support I demonstrate how, when encountering a figure with a screen reader, you won’t hear everything announced at once:
No screen reader combo treats the caption as the accessible name nor accessible description, not even for an…
Learning HTML is the best investment I ever did
One of the running jokes and/or discussion I am sick and tired of is people belittling HTML. Yes, HTML is not a programming language. No, HTML should not just be a compilation target. Learning HTML is a solid investment and not hard to do.
I am not…
Open Props UI
Presenting Open Props UI!…
Gotchas in Naming CSS View Transitions
I’m playing with making cross-document view transitions work on this blog.
Nothing fancy. Mostly copying how Dave Rupert does it on his site where you get a cross-fade animation on the whole page generally, and a little position animation on the page title specifically.
The :empty pseudo-class
We can use the :empty
pseudo-class as a way to style elements on your webpage that are empty.
You might wonder why you’d want to style something that’s empty. Let’s say you’re creating a todo list.
You want to put your todo items in a list, but what about when you don’t…
CSS Wish List 2025
Back in 2023, I belatedly jumped on the bandwagon of people posting their CSS wish lists for the coming year. This year I’m doing all that again, less belatedly! (I didn’t do it last year because I couldn’t even. Get it?)
I started this post by looking at what I…
aria-description Does Not Translate
It does, actually. In Firefox. Sometimes.
A major risk of using ARIA to define text content is it typically gets overlooked in translation. Automated translation services often do not capture it. Those who pay for localization services frequently miss content in ARIA attributes when sending text strings to localization vendors.
Content buried…
Let’s Standardize Async CSS!
6 years back I posted the Simplest Way to Load CSS Asynchronously to document a hack we’d been using for at least 6 years prior to that. The use case for this hack is to load CSS files asynchronously, something that HTML itself still does not support, even though…
Tight Mode: Why Browsers Produce Different Performance Results
Why I’m excited about text-box-trim as a designer
I’ve been excited by the potential of text-box-trim
, text-edge
and text-box
for a while. They’re in draft status at the moment, but when more browser support is available, this capability will open up some exciting possibilities for improving typesetting in the browser, as well as giving us more…
It’s a little different. For one, I’m only fetching 10 items at a time. We could push that to infinity but that comes with a performance tax, not to mention I have no way of organizing the items for them to be grouped and filtered. Maybe that’ll be a future enhancement!
The Chris demo provided the bones and it does most of the heavy lifting. The “tough” parts were square-pegging the thing into a WordPress block architecture and then getting transients going. This is my first time working with transients, so I thought I’d share the relevant code and pick it apart.
function fetch_and_store_data() {
$transient_key = 'fetched_data';
$cached_data = get_transient($transient_key);
if ($cached_data) {
return new WP_REST_Response($cached_data, 200);
}
$response = wp_remote_get('https://feedbin.com/starred/a22c4101980b055d688e90512b083e8d.xml');
if (is_wp_error($response)) {
return new WP_REST_Response('Error fetching data', 500);
}
$body = wp_remote_retrieve_body($response);
$data = simplexml_load_string($body, 'SimpleXMLElement', LIBXML_NOCDATA);
$json_data = json_encode($data);
$array_data = json_decode($json_data, true);
$items = [];
foreach ($array_data['channel']['item'] as $item) {
$items[] = [
'title' => $item['title'],
'link' => $item['link'],
'pubDate' => $item['pubDate'],
'description' => $item['description'],
];
}
set_transient($transient_key, $items, 12 * HOUR_IN_SECONDS);
return new WP_REST_Response($items, 200);
}
add_action('rest_api_init', function () {
register_rest_route('custom/v1', '/fetch-data', [
'methods' => 'GET',
'callback' => 'fetch_and_store_data',
]);
});
Could this be refactored and written more efficiently? All signs point to yes. But here’s how I grokked it:
function fetch_and_store_data() {
}
The function’s name can be anything. Naming is hard. The first two variables:
$transient_key = 'fetched_data';
$cached_data = get_transient($transient_key);
The $transient_key
is simply a name that identifies the transient when we set it and get it. In fact, the $cached_data
is the getter so that part’s done. Check!
I only want the $cached_data
if it exists, so there’s a check for that:
if ($cached_data) {
return new WP_REST_Response($cached_data, 200);
}
This also establishes a new response from the WordPress REST API, which is where the data is cached. Rather than pull the data directly from Feedbin, I’m pulling it and caching it in the REST API. This way, CORS is no longer an issue being that the starred items are now locally stored on my own domain. That’s where the wp_remote_get()
function comes in to form that response from Feedbin as the origin:
$response = wp_remote_get('https://feedbin.com/starred/a22c4101980b055d688e90512b083e8d.xml');
Similarly, I decided to throw an error if there’s no $response
. That means there’s no freshly $cached_data
and that’s something I want to know right away.
if (is_wp_error($response)) {
return new WP_REST_Response('Error fetching data', 500);
}
The bulk of the work is merely parsing the XML data I get back from Feedbin to JSON. This scours the XML and loops through each item to get its title, link, publish date, and description:
$body = wp_remote_retrieve_body($response);
$data = simplexml_load_string($body, 'SimpleXMLElement', LIBXML_NOCDATA);
$json_data = json_encode($data);
$array_data = json_decode($json_data, true);
$items = [];
foreach ($array_data['channel']['item'] as $item) {
$items[] = [
'title' => $item['title'],
'link' => $item['link'],
'pubDate' => $item['pubDate'],
'description' => $item['description'],
];
}
“Description” is a loaded term. It could be the full body of a post or an excerpt — we don’t know until we get it! So, I’m splicing and trimming it in the block’s Edit
component to stub it at no more than 50 words. There’s a little risk there because I’m rendering the HTML I get back from the API. Security, yes. But there’s also the chance I render an open tag without its closing counterpart, muffing up my layout. I know there are libraries to address that but I’m keeping things simple for now.
Now it’s time to set the transient once things have been fetched and parsed:
set_transient($transient_key, $items, 12 * HOUR_IN_SECONDS);
The WordPress docs are great at explaining the set_transient()
function. It takes three arguments, the first being the $transient_key
that was named earlier to identify which transient is getting set. The other two:
$value
: This is the object we’re storing in the named transient. That’s the$items
object handling all the parsing.$expiration
: How long should this transient last? It wouldn’t be transient if it lingered around forever, so we set an amount of time expressed in seconds. Mine lingers for 12 hours before it expires and then updates the next time a visitor hits the page.
OK, time to return
the items from the REST API as a new response:
return new WP_REST_Response($items, 200);
That’s it! Well, at least for setting and getting the transient. The next thing I realized I needed was a custom REST API endpoint to call the data. I really had to lean on the WordPress docs to get this going:
add_action('rest_api_init', function () {
register_rest_route('custom/v1', '/fetch-data', [
'methods' => 'GET',
'callback' => 'fetch_and_store_data',
]);
});
That’s where I struggled most and felt like this all took wayyyyy too much time. Well, that and sparring with the block itself. I find it super hard to get the front and back end components to sync up and, honestly, a lot of that code looks super redundant if you were to scope it out. That’s another story altogether.
Enjoy reading what we’re reading! I put a page together that pulls in the 10 most recent items with a link to subscribe to the full feed.
Creating a “Starred” Feed originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
Source: Read MoreÂ