Navigating from Search Results to Detail Screens with Cotomy

How to connect search result rows to detail and edit screens in Cotomy without turning list screens into detail state managers, including stable links, route-based detail pages, edit screens, modal boundaries, and when API-driven transitions are appropriate.

This continues from Building Search Screens with Cotomy .

After building a stable search screen, the next design question is what happens when the user selects one result. That question is not only a UI choice. It defines who owns navigation identity, who loads detail data, who owns the edit lifecycle, how browser back should behave, and how search conditions are preserved.

The baseline from the previous article is intentionally simple. Search conditions live in the URL query string. The server constructs the result list. CotomyQueryForm organizes query form behavior. Each result row exposes a stable target.

This article extends that baseline. A search result should expose stable navigation targets. The list screen should not become the owner of detail or edit state. Cotomy should coordinate page lifecycle and form behavior, but the application should choose the navigation model based on workflow requirements.

Result Rows Should Contain Stable Targets

A server-rendered result row should normally contain a real link. For example, a row might link to /sample-entities/{id}, /sample-entities/{id}/edit, /orders/{id}, or /posts/{slug}.

Those links are not decorative. They give the browser a real navigation target. They support copy and paste, opening in a new tab, keyboard navigation, screen readers, crawler visibility for public pages, and operational debugging. When a user or support engineer can point to the exact record URL, the screen is easier to reason about.

The sample entity list from the previous article can stay plain. The list page renders rows and exposes targets. It does not load the full detail model for every row.

@foreach (var item in Model.Results)
{
    <tr>
        <td>
            <a href="/sample-entities/@item.Id">@item.Name</a>
        </td>
        <td>@item.Category</td>
        <td>
            <a href="/sample-entities/@item.Id/edit">Edit</a>
        </td>
    </tr>
}

The important part is the boundary. The row knows how to identify the next screen. It does not own the next screen.

Detail Page as the Default Boundary

The simplest default is a separate detail page. The detail page owns loading and rendering one entity. The list page owns search conditions and result construction.

That separation keeps each page honest. The list page can remain a query-driven page. The detail page can focus on one entity. There is no hidden selected entity object inside the search screen that becomes stale when the browser navigates, reloads, or opens a link in another tab.

In Razor Pages, the detail endpoint can be direct.

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

public class SampleEntityDetailModel : PageModel
{
    private readonly ISampleEntityRepository _repository;

    public SampleEntityDetailModel(ISampleEntityRepository repository)
    {
        _repository = repository;
    }

    public SampleEntityDetail? Entity { get; private set; }

    public async Task<IActionResult> OnGetAsync(string id)
    {
        Entity = await _repository.FindAsync(id);

        if (Entity is null)
        {
            return NotFound();
        }

        return Page();
    }
}
@page "/sample-entities/{id}"
@model SampleEntityDetailModel

<h1>@Model.Entity!.Name</h1>

<dl>
    <dt>Category</dt>
    <dd>@Model.Entity.Category</dd>
</dl>

There is no special Cotomy requirement in this example. If the page only displays server-rendered detail, normal Razor Pages routing and server rendering are enough. Cotomy should not be pulled into the screen just to make navigation look more framework-driven.

Edit Page and CotomyEntityFillApiForm

Edit screens differ from detail screens because they usually have an interactive lifecycle: initial load, user input, submission, validation, and error handling. They also have one entity key that must stay aligned with the form.

If the edit page is fully server-rendered and uses a normal postback, CotomyEntityFillApiForm is not required. Use it when the screen deliberately needs an API-driven load-and-submit lifecycle for one entity. CotomyEntityFillApiForm is appropriate when the edit page intentionally loads one entity through the API and also submits changes through the same form lifecycle. The deeper form behavior is covered in CotomyForm in Practice , so the example here stays focused on the transition boundary.

<form id="sample-entity-edit-form"
      action="/api/sample-entities"
      data-cotomy-entity-key="@Model.Id">
    <input name="name" />
    <input name="category" />
    <button type="submit">Save</button>
</form>
import {
  CotomyElement,
  CotomyEntityFillApiForm,
  CotomyPageController,
} from "cotomy";

class SampleEntityEditForm extends CotomyEntityFillApiForm {
}

CotomyPageController.set(class extends CotomyPageController {
  protected override async initializeAsync(): Promise<void> {
    await super.initializeAsync();

    const form = CotomyElement.byId<SampleEntityEditForm>(
      "sample-entity-edit-form",
      SampleEntityEditForm
    );

    if (!form) {
      return;
    }

    this.setForm(form);
  }
});

With an entity key present, CotomyEntityApiForm-based behavior can treat the form as an update path while keeping the base API endpoint on the form.

The list screen does not need to know this edit lifecycle. It only links to the edit page. The edit page owns its form runtime.

Preserving Search Context

Search context preservation should be explicit.

For ordinary list-to-detail navigation, browser back is often enough. If the user came from /sample-entities?Keyword=invoice&Category=active, the browser history already knows that URL. The detail page does not need to copy every search condition into its own state just to support a normal back action. Standard page navigation also lets the browser use its native history behavior, including scroll restoration and page cache behavior where available. That is usually more reliable than rebuilding the same context with custom JavaScript state.

There are cases where an explicit back link is useful. A detail page may need a Back to results command, or an edit flow may redirect after save. In that case, pass a return URL deliberately rather than hiding the original list state inside a client-side object.

<a href="/sample-entities/@[email protected](Context.Request.Path + Context.Request.QueryString)">
    @item.Name
</a>

The server must validate returnUrl before redirecting. Do not blindly redirect to any received URL. At minimum, treat it as a local URL requirement so the detail or edit page does not become an open redirect path.

Another practical option is to pass the relevant query parameters back to the list link. Short-lived UI context can also be used when the workflow really needs it, but that should be a conscious screen design choice, not the default ownership model.

When Modal or Side Panel Detail Fits

Modal or side-panel detail is valid when the user must keep list context visible. Quick preview, master-detail workflows, side panel inspection, and dashboard-like screens can all justify that shape.

The boundary changes when detail is loaded into the same page. If modal detail uses API loading, the list screen is now partially application-state-driven. That can be the right design, but it is no longer the simple server-rendered navigation baseline.

Cotomy can coordinate that screen through a page controller and local elements. The implementation should still be honest about the model. A modal preview is an interactive application screen. It should not pretend to be the same thing as a normal link-based detail page.

Avoid Turning the List into an Edit Runtime

The main failure pattern is letting a search result table grow into an accidental edit runtime.

Avoid loading full detail data for every result row unless the row really needs it. Avoid embedding edit forms into every row by default. Avoid mutating records directly from the search list without clear workflow boundaries. Avoid storing selected entity state only in TypeScript when the URL should identify it. Avoid mixing server-rendered rows with unrelated client-side detail ownership unless that boundary is explicit.

Once each row starts carrying its own edit lifecycle, the developer has to reason about many active entity states at once instead of one selected screen boundary. Inline edit can be valid. But it should be designed as an inline edit screen, with its own ownership and lifecycle decisions. It should not appear accidentally because a search table kept absorbing detail and edit responsibilities.

API-Driven Transitions

API-driven transitions fit screens that are naturally interactive. Autocomplete, dashboards, private SPA-style areas, highly interactive grids, modal previews, and frequent partial refresh can all justify API loading.

That choice should be made from workflow requirements. For the Practical Guide baseline, ordinary search result to detail or edit should start with stable links and page boundaries. When the workflow needs a richer interaction model, move to API-driven transitions deliberately and make the state owner visible in the implementation.

Practical Boundary

The ownership boundary is straightforward. The search or list page owns query conditions and result construction. The detail page owns one entity display. The edit page owns form lifecycle. The URL owns navigation identity. Cotomy owns lifecycle and form coordination. The application owns the workflow choice.

This keeps Cotomy in the right role. It coordinates screens and forms. It does not need to collapse search, detail, and edit into one implicit client-side state machine.

Closing

This article extends the Guide 6 baseline. The search screen should provide stable result targets. Detail and edit screens should have their own runtime boundaries.

Cotomy helps coordinate each page, but it should not collapse search, detail, and edit into one implicit client-side state machine. When page navigation fits the workflow, use it as the baseline. When modal detail or API-driven transitions fit better, design them as explicit application screens rather than hidden extensions of the list.

Practical Guide

This article is part of the Cotomy Practical Guide, which focuses on hands-on usage patterns for the framework.

Series articles: Working with CotomyElement , CotomyPageController in Practice , Standardizing CotomyPageController for Shared Screen Flows , Building Business Screens with Cotomy , Handling Validation and Error Display with Cotomy , Building Search Screens with Cotomy , and Navigating from Search Results to Detail Screens with Cotomy .

Next

Next article: a future Practical Guide article will cover edit screens without state drift, focusing on load and save paths, form baseline state, and UI/server state synchronization.

Links

Previous: Building Search Screens with Cotomy . More posts: /posts/ .

Related Articles

Building Search Screens with Cotomy , CotomyForm in Practice , Building Business Screens with Cotomy

Learn Cotomy

Cotomy is a DOM-first UI runtime for long-lived business applications.