There does not appear to be a way built in way to server render the item that would be created by a x-for template and have Alpinejs recognize them as the items from them template.
I have created a codesandbox that shows the current behavior here: https://codesandbox.io/s/current-server-rendered-list-ucqqy the items are added immediately after template.
I have gotten around it by creating a script that sets the __x_item_for property of the element, and then adding the template to the DOM. Here is the codesandbox an example of how I would like it to work https://codesandbox.io/s/current-server-rendered-list-bound-on-client-ko4l0 . And while this works and could easily be built into a server side component to take care of creating the script, it relies on an implementation detail that may change later.
Unless I missed something obvious, if your items are built server side, you don't need an x-for.
if they come from an api call or they are a mixture of serverside rendering and client side interactions, you need to print your server side items as json in a js variable and you can use that variable in your x-for.
The approach you described sounds like a dangerous mix of backend and frontend concepts which wil be a nightmare to debug in six months time. I wouldn't recommend going down that route.
Basically I want to always be rendering out the latest version of a page, that way it is indexable by search engines and social shares. But I don't want users of that part of the app to need to refresh to see changes that admins make to the contents of the list.
I will work on a more relevant sandbox to illustrate it after work.
I think I see your concerns but search engines usually index dynamic contents generated by JS.
https://developers.google.com/search/docs/guides/javascript-seo-basics
This one is from Google but I'm pretty sure other search engines do the same (otherwise other JS frameworks would have the same issues).
Social shares are usually driven by metatags so the content of that list shouldn't be relevant, I think.
if you really need it for some other reasons, you can probably do something more complicated like:
https://codepen.io/SimoTod/pen/XWbMbXN
Bear in mind that this is just an example and I would not consider it as a best practice.
My preferred way would be to return the JSON and let Alpine deal with that.
Other people might have better solutions for your use case as well.
@SimoTod
The approach you described sounds like a dangerous mix of backend and frontend concepts which wil be a nightmare to debug in six months time. I wouldn't recommend going down that route.
Can you expand on why this will be hard to do? I understand it isn't common practice, but I think there is a promising future here and would like to understand the problems here more before going further
My main reasons are
1 - I want to do something like an SPA but I want to spend most of my time on the backend
2 - I want my data to be in sync on every page load while still having easy access to all the application state
3 - I want the productivity gains of backend mvc frameworks like rails and pheonix
I started to implement it on my local and put it up on my fork here https://github.com/slynch13/alpine/tree/xforserverrendered but have not had time to look updated my repo with the latest changes to the for directive so am not doing a pull request yet.
In what I did I had used a r-for-key attribute, but thinking about it an attribute like :forkey= might fit better with the framework.
The other advantage you get with server rendering is that you have a much faster time to first render, and no jumping around of elements as the for items are rendered. Here is a sandbox that demonstrates it https://codesandbox.io/s/custom-alpine-with-list-item-binding-2gk3p .This sandbox demonstrates the main reason I really would like to have it, and while @SimoTod 's solution of using an x-if to switch out the server rendered works, it makes the server code needed more complicated. I also do not know if screen readers would end up reading it again.
Can you expand on why this will be hard to do?
@MarcelRusu it's not about being hard, but it sounds like something that needs a strong pitch to be accepted in the core.
Let's pretend you have a list of 3 objects with a name property. You want to print them in your page from the backed as html and you want them to be in a x-for. You also want to add an input for each element and bind its model to the name property. If you don't put your server item into data as json but you just print them in page, it won't work easily (it will require a lot of custom code).
There might be a smarter solution and it could be that @slynch13 's PR will be accepted but we would need to document it clearly. My gut feeling is that it doesn't reflect the design choices of the library but I might be mistaken.
@slynch13 I haven't tried screen readers, i don't think they'll read it twice since you only have a copy of the list at once but you can drive it using aria-attributes if needed. That being said, the solution is really hacky, i mentioned that wasn't a good one. I wasn't even sure if it was worth posting but I did iy to show the complexity of a mixed solution, even a basic one.
If you have strong points, you can PR it and pitch it to Caleb, otherwise you can just mantain your fork which fits your requirements. I think you choose the right path. 馃憤
interesting - looked at the pr, not what I had in mind.
@SimoTod the direction I was thinking was just load the initial data from the server and convert to json and work in alpine completely. But you do lose a lot of server side capabilities.. I don't really have a clear architecture panned out in my head yet but I feel like there's a lot of interesting work to be done in this space, and alpinejs I think is on that path. Super excited for the future of this :)
@SimoTod Not sure I have any stronger points then what I have already made.
I think I will go with something closer to my original codestandbox, AlpineJS already supports most of what I need with the exception of the x-for. For example <div x-data="{text:'Some Text'}"><span x-text="text">Some Text</span></div>already works exactly how I need. So rather than needing to apply the changes each time I want to use a newer version, I will just set up some tests like what I have in my fork that run when new versions of AlpineJS are released so I know if I need to fix something before upgrading.
@MarcelRusu I think AlpineJS will already support that scenario.
Tested out the screen reader built into Mac and the pen you posted above does not read the text twice. And since I am generating the client code anyways your solution actually works fine, so I am closing this issue.
@slynch13
Would it be possible that instead of forking whole alpine repo you created "plugin" that would add such functionality to it?
@piotrpog an example of how you could do this is here https://codesandbox.io/s/current-server-rendered-list-bound-on-client-ko4l0. But it uses an undocumented API in the __x_for_key field added to the DOM element. They can change that at any time, and break the "pluging" so I have decided to go with the way that @SimoTod has suggested, and remove the prerendered content after with the x-init.
It would be nice though if they had something like an x-removeoninit directive that would remove nodes after the init, just to make the code cleaner. @SimoTod what do you think is the likelihood that a pull request for something like this would be accepted?
The only reason I can think about it's to support browsers with javascript disabled (do they really exist nowadays? 馃槀)
In my mind, it doesn't fix anything that is seen as a problem since there's no harm in dumping your list in a js variable and letting Alpine render it but I might be wrong and if many people need it, it could be accepted into the core.
It mostly depends on what @calebporzio thinks about that.
When an official plugin system is available, we will be able to write plugin like that one without worrying about being accepted into the core so it's probably worth to push for that feature instead.
I think there is already an open issue for it.
@slynch13 I don't think it would at the moment, this isn't really something that belongs in core Alpine since you can do it using code in your component & Caleb wants to add custom directive support to Alpine at some point.
Yeah, custom directive is a potential item on the roadmap for v3.
Bing still recommends server side rendering because it looks like they still don't support it, https://www.bing.com/webmaster/help/webmaster-guidelines-30fba23a.
Site technology
The technology used on your website can sometimes prevent Bingbot from being able to find your content. Rich media (Flash, JavaScript, etc.) can lead to Bing not being able to crawl through navigation, or not see content embedded in a webpage. To avoid any potential issues, consider implementing a down-level experience which includes the same content elements and links as your rich version does. This will allow anyone (including Bingbot) without rich media enabled to see and interact with your website.
I tested it in both the fetch as googlebot (which works) and fetch as bingbot (which doesn't work).
@SimoTod @HugoDF I would much prefer a way to associate a pre-rendered DOM element to a x-for or x-if template over an x-removeoninit attribute anyways.
I'm not really that interested in the search engine part anyways honestly. But I would really like for my web app to still work, , if one of my customers clients either had javascript disabled, blocked or there was an issue with the CDN and alpinejs didn't load even if it is a static experience.
I look forward to when custom directives are in AlpineJS and I can create a progressive enhancement enabled versions of x-for and x-if without needing to either fork it, or add brittle code. Those two are just work really.
Yeah, I understand but i can't really think of a nice way to achieve it with the current implementation based on template tags. Hopefully the plug in system will make it easier.
Most helpful comment
I think I see your concerns but search engines usually index dynamic contents generated by JS.
https://developers.google.com/search/docs/guides/javascript-seo-basics
This one is from Google but I'm pretty sure other search engines do the same (otherwise other JS frameworks would have the same issues).
Social shares are usually driven by metatags so the content of that list shouldn't be relevant, I think.
if you really need it for some other reasons, you can probably do something more complicated like:
https://codepen.io/SimoTod/pen/XWbMbXN
Bear in mind that this is just an example and I would not consider it as a best practice.
My preferred way would be to return the JSON and let Alpine deal with that.
Other people might have better solutions for your use case as well.