silviurosu

silviurosu

I have a liveview app that has some routes. Let’s say I have a route as an example:

scope "/", MyAppWeb do
    pipe_through [:browser]

    live_session :current_user,
      on_mount: [{MyAppWeb.UserAuth, :mount_current_user}] do
      live "/:user_id", ProfileLive, :show
    end
  end

For SEO purpose I what to have shorter and seo friendly routes.
Instead of clasic paths like this:

/search/city/chicago
/profile/dentist-john-doe

I would like to have it like this:

/chicago
/dentist-john-doe

Where the first path should render a search result for all the specialists in Chicago city and the second route will render the profile of a specific specialist.
I would like to receive that parameter, do a query in DB first to see if I have a specialist with that id. Then take a decision: If I find the specialist render specialist page, if not I search if I have a city with that id and render the search result page for that city.
Is this even possible in liveview?

Showing Posts 1 to 10

sodapopcan

sodapopcan

There may be a more elegant way to do this with plugs—I never actually figured out a way to mount different LiveViews at the same path at the router level (though I didn’t try very hard). One way I’ve done roughly what you’re describing in the past is to have one LiveView and use LiveComponents for the templates/callbacks. Something along the lines of this:

defmodule MyAppWeb.PageLive do
  @impl true
  def mount(%{"id" => id}, _session, socket) do
    record =
      with nil <- MyApp.Specialists.get_specialist(id) do
        MyApp.Cities.get_city(id)
      end

    {:ook, assign(socket, :record, record)}
  end

  @impl true
  def render(%{record: %MyApp.Specialists.Spcialist{}} = assigns) do
    ~H"""
    <.live_component id="specialist" module={MyAppWeb.SpecialistComponent} specialist={@record} />
    """
  end

  def render(%{record: %MyApp.Cities.City{}} = assigns) do
    ~H"""
    <.live_component id="city" module={MyAppWeb.CityComponent} city={@record} />
    """
  end

  def render(%{record: nil} = assigns) do
    ~H"""
    <div>Not found</div>
    """
  end
end

In your router you need a catch-all route after all other routes:

live "/:id", PageLive, :index

I suppose you could also do this using a classic controller which dispatches to different LiveViews using live_render in a similar way as above, then you don’t have to use LiveComponents.

It’s probably also better to move the with logic into your domain layer and having something like record = MyApp.Search.find_specialist_or_city, but not a big deal either way depending on how complex your app is.

LostKobrakai

LostKobrakai

Afaik the idea that URL length does matter for SEO is a big myth. Especially with people interacting less and less with URLs to begin with I probably wouldn’t care.

The issue with (router mounted) LVs specifically is that on establishing the websocket connection is needs to be able to find the live … route using Phoenix.Router.route_info, which doesn’t work well with your idea of having the route be dynamically determined. You could generate the routes at compile time to work around that, but it doesn’t sound like you know the short urls at compile time. Knowing them only at runtime you’d end up with what @sodapopcan described, which works, but is not an architecture that’s meant to be used.

A simpler way to accomplish both should be sticking to the existing live routes and have a controller, which redirects the shorts urls to their long ones.

olivermt

olivermt

Google cares much more about the semantics, including in urls these days. As long as you dont duplicate (keep urls canonical) content, the first set of urls you showed are much better.

silviurosu

silviurosu OP

Thanks guys for feedback.
I decided to stick with liveview routes even if the path has 2-3 levels instead of trying to make it one level and doing a lot of magic. I’ll keep my fingers crossed when I’ll release it live.
I will add also the canonical url to point to the same page to not have duplications in case of extra parameters.

sodapopcan

sodapopcan

How would you deal with a situation like github usernames where the username is the root path segment?

Otherwise I’ve only used my solution for an artsy site where I literally only had two routes: / and /:slug. I should have caveated that, though I’m still curious about your thoughts on the username thing. My solution would, of course, only be looking for :user_id at the bottom of the router, not returning a variable resource.

LostKobrakai

LostKobrakai

Dynamic segments which result in the same “kind of page” are fine. The thing I meant to say being discouraged is having a single LV manage multiple “kinds of pages”. And the edge between those certainly are not clear cut. Even usage of live action I’ve heard being discouraged by chris mccord, unless there’s a reason for the pages to be related, like the modal case, where the modal section still renders the underlying page as well.

sodapopcan

sodapopcan

Awesome, thanks for clarifying!

I actually didn’t know that about @live_action though that is the only case I use it for: routable edit modals.

egze

egze

Source? :slight_smile:

LostKobrakai

LostKobrakai

Somewhere deep in the depth of inaccessible slack conversations.

pxp9

pxp9

Could you put a toy example for this ?

Thank you in advance.

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
apz
I’m new to elixir and just tried to install the elixirLS extension for VScode(ium) and it is throwing some errors that I would like help ...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews