![](BLOG/2025/05/attachments/dynamic-obsidian-publish-homepage-dataview-templater.webp) ### The Obsidian Publish Homepage Dilemma Lately, I've spent some time customizing my blog. As I mentioned in a recent post ([Starting Over with Obsidian - My Blog Finds a Home on Publish](BLOG/2025/05/Starting%20Over%20with%20Obsidian%20-%20My%20Blog%20Finds%20a%20Home%20on%20Publish.md)), Obsidian Publish has a few limitations. One of the most annoying is the lack of a native way to manage a dynamic homepage. What does that mean? Simply put, if I want to display my latest published posts on the blog's front page, I'd have to manually edit the page each time. Unless... ### The Solution: Dataview and Templater Unless you use a little trick. A trick suggested to me by [Joschua's](https://github.com/selfire1) enlightening post: [Using Dataview on Obsidian Publish](https://joschua.io/posts/2023/09/01/obsidian-publish-dataview/). In a nutshell, we need two essential Obsidian plugins: - [Dataview](https://blacksmithgu.github.io/obsidian-dataview/), *a live index and query engine over your personal knowledge base* - [Templater](https://silentvoid13.github.io/Templater/), *a template language that lets you insert **variables** and **functions** results into your notes* ### Querying Your Vault with Dataview With *Dataview*, I can query my Obsidian Vault to create, for example, a list of my latest posts. The syntax is quite intuitive and very similar to `SQL`. Here's a small code snippet: ```sql TABLE without ID dateformat(created, "yyyy.LL.dd") AS Date, file.link AS Post, language as Lang FROM "BLOG" WHERE publish AND !contains(permalink, "--hidden") SORT created desc LIMIT 10 ``` And this is the result I get inside Obsidian: ![](BLOG/2025/05/attachments/homepage-dinamica-obsidian-01.webp) Obviously, the code is tailored to my needs. My blog posts are mainly in the `BLOG` folder. However, I decided to show only those specifically written for the blog on the homepage, excluding other shared folders like `LEARNING`. I've also added two specific conditions: - I only want to show posts that are actually published online (property `publish === true`). - I don't want to show posts that have the string `--hidden` in their URL (these are "service" posts I'm not interested in displaying on the homepage). I limit the table to 10 results (`LIMIT 10`) and sort them in descending order by creation date (`SORT created desc`). ### Overcoming Publish Limitations: Enter Templater The problem with this solution is that, while it works perfectly in my local vault, it has no effect once the page is uploaded online. Obsidian Publish, in fact, requires static pages, whereas *Dataview* calculates the table dynamically on each load. To solve this snag, I had to turn to *Templater*. This plugin allows you to "freeze" the result of the *Dataview* query into a static Markdown file. ### Creating the Template for Automatic Updates So, I created a new template, imaginatively named `PUBLISH BLOG`. Let's look at the key steps. #### 1. Connecting to Dataview First, I create a variable to communicate with the `Dataview API`: ```js const dv = app.plugins.plugins["dataview"].api; ``` #### 2. Defining the Queries Next, I add a JavaScript [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) JavaScript. The first element of each map entry is the filename where the query result will be saved; the second is the query itself. This is the query I'm currently using for my homepage: ```js // Add as many filenames and queries as you'd like! const postsAndNotes = new Map([ [ // Output filename "recent-new-post--hidden", // Query Dataview 'TABLE without ID dateformat(created, "yyyy.LL.dd") AS Date, file.link AS Post, language as Lang, description as Description, "[![](" + cover + ")]" + "("+replace(file.path," ", "%20")+")" AS Cover, map(tags, (x) => "#"+x) AS Hashtags FROM "BLOG" WHERE publish AND !contains(permalink, "--hidden") SORT created desc LIMIT 10', ] ]); ``` #### 3. Specifying the Destination Folder I also need to tell the plugin which folder to save the generated file in: ```js const folder = "BLOG/RESOURCES"; // Make sure this folder exists ``` #### 4. The Complete Script to Save the Result Saving the result statically requires a bit more work. The main logic is contained in an asynchronous function that iterates over each element of the `postsAndNotes` Map: ```js await postsAndNotes.forEach(async (query, filename) => { // Check if the file exists, otherwise create it if (!tp.file.find_tfile(filename)) { await tp.file.create_new("", filename, false, folder); new Notice(`Created ${filename}.`); } // Find the file (now we know it exists) const tFile = tp.file.find_tfile(filename); // Execute the Dataview query and get the Markdown output const queryOutput = await dv.queryMarkdown(query); // Prepare the final file content, including the desired frontmatter const fileContent = `---\npublish: true\npermalink: "${filename}"\n---\n%% update via "Update Publish Files" template %% \n\n${queryOutput.value}`; // Overwrite the file with the new content try { await app.vault.modify(tFile, fileContent); new Notice(`Updated ${tFile.basename}.`); } catch (error) { console.error("Error updating file:", error); new Notice("⚠️ ERROR during update! Check the console. File skipped: " + filename , 0); } }) ``` ### Streamlining the Workflow: One-Click Publishing Am I done? Yes and no. Yes, because this would be enough. No, because I'd have to remember to run this template every time I modify the blog. However, I can optimize further: I created a function that, after updating the files, automatically opens the Obsidian Publish window: ```js const openPublishPanel = app.commands.commands["publish:view-changes"].callback; if (openPublishPanel) { openPublishPanel(); } else { new Notice("Comando 'publish:view-changes' not found.", 0); } ``` Why do I do this? Because this way, I just need to get used to running this template with every change and then publish directly. In practice, with a single "button" (running the template), I get both the dynamic update of some blog pages and the opening of the publishing panel. It might seem trivial, but it significantly simplifies the workflow, eliminating the need to remember all the steps. For those interested, here's the link to the complete template I'm using: - [Obsidian - Templater for PUBLISH BLOG](LEARNING/SNIPPETS/JAVASCRIPT/Obsidian%20-%20Templater%20for%20PUBLISH%20BLOG.md) ### Beyond the Simple List: Customizing the View with CSS However, an aesthetic "problem" remains. As much as I like the idea of having my posts displayed as a simple tabular list, it's perhaps more interesting and functional to use a more classic layout, with a cover image, title, short description, list of tags, and publication date: ![](BLOG/2025/05/attachments/homepage-dinamica-obsidian-02.webp) Fortunately, I can achieve this result simply by using some CSS styles. The only trick is to identify the correct selector to apply the styles only where needed. #### Identifying the Correct CSS Selector In this case, choosing a descriptive name for the file I create with Templater comes in handy. Since I import the same file into my homepage, I can leverage the fact that Obsidian Publish displays the content within a `span` tag with an `alt` attribute equal to the page name. The following image illustrates how the element appears in the browser's developer tools: ![](BLOG/2025/05/attachments/homepage-dinamica-obsidian-03.webp) I can then use the CSS selector: ```css span[alt="recent-new-post--hidden"] ``` to target the elements I want to act upon and lay them out as I see fit, transforming the table generated by Dataview into a card layout, for example. I won't include all the CSS code I used here. However, if you're interested, you can find it on this page: - [Obsidian - Style for Recent Posts in Obsidian Publish Blog](LEARNING/SNIPPETS/CSS/Obsidian%20-%20Style%20for%20Recent%20Posts%20in%20Obsidian%20Publish%20Blog.md) ### Conclusions (For Now!) Well, that's all for today! I hope this detailed overview of my customization process can be helpful to other Obsidian Publish users. See you around! 👋