← All Talks
Presentation Development and Protocol

Social Components

Dan Abramov · @danabra.mov
Sunday, March 29, 2026
1:30 PM – 2:00 PM PT
Great Hall South
Available in-person & via livestream — Stream 1 (Great Hall South)

What if social products were remixable?Imagine you could take a social product and break it its user interface into pieces. Then imagine that you, or anyone else, could recombine those pieces in different ways, swap things out, composing and remixing experiences made and hosted by different people.We already know a way to compose UI: Components. However, we didn't have a way to compose components across products. Atmosphere gives us that way: lexicons define component contracts, records point at the endpoints, everyone uses the same data.So let's put components on the protocol! In this talk, Dan will present Inlay—an experimental browser for remixable cross-product server-driven user interfaces on the Atmosphere. You can think of it as "React for atproto" or like "HyperCard for social".Whether it's a terrible idea or a glimpse of a post-app future remains to be seen.---(Demo teaser: https://bsky.app/profile/did:plc:fpruhuo22xkm5o7ttr2ktxdo/post/3mdjhy2bofk2h)

Can anybody hear me? Okay. Hi.

I I couldn't find the water, so today it's gonna be coke. So hi, uh, my name is Dan Abramov. I used to work on Bluesky for a little bit and on React for a little bit before that. Uh and right now I'm just doing random stuff. Uh and so I you know, some projects you start because you have like a real problem that you want to solve. Uh this is not one of those projects. So this is one of those projects where like I just had this idea that you know I know React pretty well, and like I know how components work.

And I also kind of know Ethprato at this point. And I just had this idea like what if you put components on Ethroom? And I just couldn't like kind of get rid of this idea, and at some point I decided okay, I just I just need to try and see uh where that leads. So I vibe coded a prototype uh like a couple of months ago in why in one week uh and it kind of looked like this. Uh I ended up spending the the next two months completely rebuilding it like properly. Uh but I'll just show you the prototype first and then I'll kind of show you how it works.

Um what you see here is uh I guess it's kind of like um it's kind of like a Bluesky profile page. So I'm just using that as an example. Um, you know, it has like different tabs, it has the you know the replies tab and the media tab, uh it has pagination, um has this current component. Uh, you know, there's uh there's this like third party uh website uh that uh has this feature that lets you see uh kind of social connection uh and like jump between people and uh you know see see who kind of interacted with whom recently, which I think is a really nice uh you know kind of thing you can do when the data is open.

Um one frustration for me with a lot of apps like with app paradigm in general, is that uh things like this have to live somewhere else. Like if you there's really no point in continuum between uh, you know, here's like a one-off tool that I throw away that like lives on some URL somewhere that is like not discoverable, and here is like a whole app. And there really isn't much uh like in the middle. And so I thought I thought it would be interesting if there was a way uh for you know people to kind of prototype uh features like this uh in a place where you can find them.

And so maybe let's uh let's have a look at how this actually works because so far, you know, it just looks like kind of like a worse, uh a worse browser. Uh but one interesting thing we can do is uh I can click uh inspect on it. And it kind of uh opens up a little bit. You can see you know that actually like what I've been looking for is um it's kind of like a tree, kind of like a React tree, except each of these things is actually a Ethroto record somewhere. Uh so these are different components that could be implemented by different people, uh could live in different places and so on.

Uh and maybe let's uh kinda pull it out a little bit. So this is my uh profile header, uh and this is profile posts and profile posts is uh probably you know you you wouldn't be surprised that it's a list of posts and a post itself, you know, it has like uh avatar and handle yeah. We need to go deeper. Uh yeah, so uh okay, so what what you know what can we do with it? Um so one thing I I I want to do is um I'm actually gonna I'm gonna open this as a different user.

So I I have a like another browser here. Uh let's take this profile header. Uh so I'm now logged in not as Dan Abramov, but like as as another user. And I have this remix button here. So I'm gonna press uh remix and I hope that it works. And so this opens up sort of an editor. Uh so this is the profile header. And you can see there's a bunch of something that looks like JSX here, but you know, really it's JSON. That's what JSX has always been. Uh and uh I can actually look at like another way to look at it is just on PDSLS.

So I'm gonna open uh PDSLS and I have this uh inlay component collection and I I have this profile header. And so this stuff that you see here, like there's like a stack with like avatar and like title and caption and like all of that jazz and like the cover uh it's actually like you can also see here that this is this JSON structure uh which is just like a UI tree and it imports from some DIDs which are this is like the built-ins this is the design system and these are actually like my other components so it's kind of like did act kind of like packages.

And here it says that this component can act as a kind of a view of the profile collection. So it's it's like a thing that can render a profile at least one of those things. And so I'm gonna remix this a little bit I'll uh I think I'll get rid of the cover and actually like maybe you know maybe I'll get rid of the avatar to instead I'll add a I'll add a SCR component. So you've seen uh the SQR before so I'll just I'll just throw that in here so that it's it's easy to find.

I think I'll actually uh let me put it here and I'll make this a row instead of yeah and maybe maybe I'll add some inset. Okay so this is like my take on what a profile header should be. So there's already a profile header component uh but I'm gonna publish uh another profile header component from uh kind of my perspective from this user's perspective and so this you know this doesn't change like what what we're looking at here but actually like again you know I can switch between them so here's here's the new profile header component from you know my other user uh and you you know you can see there are like all these different ways to look at the profile right like what what what makes a profile a profile even like something like PDSLS shows it.

And so okay so I made this profile header but uh this profile component written by somebody else it doesn't use that header right but what I can do is again I can click remix here and so now we're gonna wait is it is it going to the wrong thing I think it's going let me just refresh just in case. Yeah it went to the wrong thing. But here's the uh so here is the the profile component itself that contains you can see it has the profile header it has the tabs with like posts and replies and kind of uh you know delegates to those it has the SQR tab.

I'm not gonna need that because I move I've moved the SQR you know into and I guess Paul doesn't listen to music uh so it doesn't have TLFM place. I'll just get rid of that too actually for now. Yeah and so but uh now we have two versions of the profile header right there's one by the Nabramov which is the original one it's here and then there's profile header by my user which is just created. And so what I'm gonna do is I'm gonna slide this a little bit up so that it takes precedence. So now the profile header is gonna refer to the version that you know that I remixed from my user.

So it's it's kind of like a low stakes way to like play around with okay this is what I think a profile page should be. So I'm just gonna you know I'm just gonna publish this as profile and again the you know it's it's it's just an option here. So now we can see that this is this is my take uh you know my user's take on what a profile page could be okay so uh of course we don't want to always create everything from scratch unless you're using LLMs I this project is like very caveman mode I'm sure there's like a way to get LLM to do this as well and I don't know if it makes sense but I'm just I'm just showing you uh kind of the vanilla version uh and uh so canvas you know this thing I'm calling it like a canvas right it's just like this this thing where you can kind of jump around and uh kind of see different things and pull them apart uh but one probably that that's my first prototype but I realized that okay like how do you actually discover these components that other people create and that's you know that I made the editor but then the question was where do you find them?

So I realized I needed something like a browser too. That's uh kind of like I guess kind of like PDSLS right so we uh we could say you know if you've ever uh if you've ever seen PDSLS you know this interface where like you select the um you select the user and then you select the collection type uh and you can just like navigate between records right so this is kind of PDSLS inspired. But maybe you can like register this like other ways to view the same data, right? That are like more human friendly. And I'll just show you like the process for what you know what it feels like to create one of those.

So I'll say I'll go to profile. And again, like just a profile record. Like looks like this. This is a built-in component. I'm gonna create a new one. It gives me this template with like stuff that's available. And I'm just gonna say, okay, like let's say I want uh I want an avatar and it knows that oh like there's already a dit here, so it perfills it. And I want like a uh you know, I want like a title uh with let's say like pulse uh like display name, right? So we put display name here and let's make this like a oops, oops, oops, a row.

Okay, so I'm gonna publish this as like profile example. Okay, so here's my profile uh where is it in the list? I don't see it in the list of somewhere. Oh, there it is. So here's my profile example component. Uh let's check that it appears in uh my other user's PDSLS. So uh there it is. Profile example, you can see the the tree and stuff. So there are uh three types of inlay components. So this kind of component is called a template component because the uh the actual JSO for how to for the UI lives directly inside the record.

Uh so you know if uh inlay dies, you can you know uh you can just interpret the same data and kind of bring it back to life. Um but it's very limited, right? Because I I can't have this is not JavaScript, this is just like a JSX editor, it's just like it could be it could have been like a block editor or whatever. Um but it doesn't have like loops, it doesn't have conditions, it doesn't have like any fancy, I can't you can't like hit another app view, another service or whatever. Uh so it's a bit limited.

Um but the one thing we can do is we can eject. Really like this feature. I build it into everything. Okay, wait, I can't eject from this one because I haven't uh I haven't created token. Let me use uh let me just do the same example from my user where I already connected. So there is uh there is this thing called Val Town. I don't know if you let me delete this. Uh call this profile example. Yeah. So there's a there's this thing called Valtown, which is like a uh kind of simple coding um environment for publishing some server code, kind of like REPL for you know that that lets you like run a server.

So when you press eject on a component, what it's gonna do is it's gonna hit the Valton API, which is for some reason taking like five seconds. Uh and generate what they call a vowel. So a vowel is just you know, it's just the server. And I I made some helpers for this thing, and like these Alexicons, so it it like gets type checked. Um but this is you know, this is my component. Like I just I just moved it from here, now I can like call it. So it's an XRPC call uh to that component, which means that I can uh you know I can treat it as code, I could make like uh different you know API calls here or whatever.

Uh and let's see. Yeah, you see it it it updated, so it works. Uh all right. No, I need to jinx.

So let's uh let's build another fun little component. Um so let's go to this uh what is it uh no the the U and me connection thing that everybody's been using at the conference. Uh well, spoiler, I already already made it. Uh well let me let me just delete it so I can demo it again. Uh where is the why can't I delete it actually? Is it from a different oh I made it from a different account. Wait one second, I'll delete it. Don't uh close your eyes uh so that you haven't seen it, and then I'll just make it again.

Okay, it's gone, it's gone. Uh so we're we're gonna make a little component. You know, like when you look at PGsl S version of these like connections, it's like what is going on here, subject, DID, and and and so on. So uh let's see, we're gonna create a little a little component that I'm gonna take the date of myself you know from the record and let's let's show uh maybe like uh like an avi handle. Maybe it's not a bad idea, and then let's take the date of the subject and maybe also show an avi handle and maybe let's like just just do some emoji like high five.

And yeah, so and we're and let's say we we still add a timestamp. Okay, so we're gonna publish this as a connection. So now you can see, you know, you can see what's been going on. Uh yeah. Uh let's see who I said hi to Yeah. Okay, so this is working. Um let's go back to the uh let's go back to the profile. So I kinda have this uh display ground where I can you know kind of just play with different components kind of reef on them. Um it's it is a bit tedious sometimes, like if I want to like maybe I just want to change something globally, right?

Like in npm, like the model so far I'm following is similar to npm, but like each component has dependencies. And so like if you want to change something, you fork that component, and then you kinda have to fork upwards dependency chain. Uh but for example in npm there's this feature uh like uh resolutions at the top where you can just say, you know, for everything, like I actually want to globally make something different. So for example, like let's say uh you know, like yesterday Paul was showing this example with uh removing uh the handles and just showing uh the uh display names uh way ahead of you.

Uh well the truth is I actually haven't built a handle component, so I just I literally can't make the handle work. Uh but I have a different idea, so I I kind of want to uh remix this avi handle thing. And specifically um, you know, I uh I want to uh democra democratize uh education. So I want to make us all PhDs. So I'm just going to put PHD here. And I'm gonna publish that. But now the problem is okay, you know, like you want things to be predictable, so you don't really want like everything to just fall apart because like you change one little thing.

So you kinda want to be like intentional uh about like do you want to like override something? And so I'm not gonna like fork all the way to the top. Instead I'm gonna use something slightly different. So I have this like stack feature here, which is uh kind of like a global resolution mechanism, which has like first you know you go through whatever the component author specifies and then you can fall back on like my versions of components if someone missing. But I can actually drag that up and say, whoa, wait, I okay that was that was not intentional.

Uh but we can pretend that this didn't happen. So uh you can you know you can say that I want to prefer uh this version uh that that I you know of any component. And so when I when I drag this thing above, it means that it will prefer my versions of a component throughout the whole tree. So this is why uh everyone is a PhD now. Great. Um okay, we're we're gonna run out of time soon. I just want to um I want to see why this broke because I kinda wanted to show this. So uh connection resolve type connection.

Uh so is it called did I not save it? Oh, it has a different NSID. I created it, yeah, I created it from a different user. Uh okay, I'll just I'll just I'll just publish it from from uh. Yeah, I did not anticipate this. Uh one second, let me delete this and uh this is published by it's not easier to to be two different users at the same time. I'm just delete this as if it didn't exist. And I'm gonna I'm just gonna create this again from my real user because that's that's what I'm already hard coding somewhere else.

So this is my connection component. Okay cool. And the uh yeah so the other thing I wanted to show here is um so feeds right uh like in feeds uh we like feeds are another area where there's like uh some providers of uh like the actual like feed content but the format of the feed itself is different determined by the app and you can't really experiment with like what goes into a feed uh if you're not an art an app author so something uh something I wanted to show here is that uh actually like if you look at the post list component uh where I'm that is responsible for showing you know this list of posts if you look at its source um which is here like profile posts for example so if you look at its source uh the way it works is that it hits uh some JIT uh with uh with a query right so I'm calling this like get uh profile post page and I have this uh you know in my val town I have uh like I have a service run in that is uh uh wait then this is not the one this one so this is this is the thing that give that generates the profile page uh the the profile posts and you can see that it hits the bus API and then it just returns UI components right so this is how this is how we see uh you know like this stuff is coming from here and similarly the uh the media tab where we see a grid of those images like it's it's not like hard coded it's also like it's just a component hidden a list of get profile media page and here it's like a bunch of blobs that are getting clipped uh as as squares or like get TLFm place uh on well again uh poll doesn't listen to music but uh if you go to my profile uh yeah you can it's like everything is a little browser uh yeah if you go to my profile and look at plays you see like it's this is this is TLFm place and it just uh you know this is how it works it just fetches the collection and renders a tiltil an array of chill play components but you know what if you want to experiment with the content of those feeds themselves and so uh actually each of these you know like I provide these endpoints here but these are also dynamically bound so the way it actually works is that in my PDSLS there's a uh there's an endpoint collection that says to get uh you know get profile posts page uh gets resolved to a service running here which is you know this code and so this is how it renders an array of posts uh but what my other user does uh so my other user uh has uh also has an endpoint record for the same endpoint and it says actually let's hit this other you know let's hit this other service and this other service is uh here it is so this is a different implementation of the get profile posts page and here it fetches the posts but it also fetches the U and me connections and you know and the element is just like a connection uh the connection um component that I created earlier and it kind of just interleaves them and so this is uh this is when you know when I prioritize uh sorry in here when I prioritize my other stuff in the stack then this is why it's gonna just yeah like it's just gonna show them inline so this is something that you can also experiment with.

Yeah and you know this is this is uh this just a prototype like I I don't know if it's I I may like I've coded like a SwiftUI client for it. So the uh I said there are three uh three types of components there's template components that are the UI lives in the record uh there is external components which are you know the ones that uh are ejected which hit like a service or like uh like a val or your own server or whatever and the third type is built in and so the you know when you looked at the um you can see this are these are the red ones are overwritten uh from the stack uh so it's like I want to do something different here.

So um you can see that here, like these are these are the built-ins. So they don't have any implementation on the on the protocol. This is something that like the design system implements. And I you know I have my own design system that is uh there's like what I'm using here, all this like row, caption, avatar, all the stuff you've seen. But in principle, you could make you know you could fork my I just open sourced uh inlay, so it's uh it's on 10 guilt. Or is it 10 guilt uh inlay. Yeah, there it is. Um so the app is also open source.

Uh so you know you can fork it, you can make your own version. You can decide to have a different set of primitives. Uh and uh to uh like I I made these primitives to be like cross-platform, so you could make like a Swift UI client for them or something else, but you could make them like closer to HTML if you want. And yeah, this is just an idea I'm kinda throwing out. Like I'm probably not gonna like maintain this forever. Uh, but it's on the protocol, so in some sense it is maintained forever. So thank you.

Oh I forgot. Uh yeah, it's it's it's called inlay. It's like inlay.et. Yeah, this is this is this is how you find it. Okay, sorry, bye.