reactjsshopifyremix.run

Remix flat routes return 404 on Shopify app except for index.jsx


I'm building a Shopify app using Remix without any third-party routing plugins. My project structure follows Remix's flat routing convention. For example, I have this folder structure:

custom-signs
└── app
    └── routes
        └── app.customisers
            ├── $customiserId.jsx
            ├── index.jsx
            └── new.jsx

Only index.jsx loads correctly in the Shopify dev server. When I try to visit /app/customisers/new or /app/customisers/123, I get a 404 error, even though those files exist and are named according to Remix's flat routes spec.

To clarify:

I am not using any third-party plugins for routing.

The app works fine for other routes like /app, /auth.login, etc.

It seems like Remix is not picking up the subroutes under app.customisers.

Is there something I'm missing in how Remix resolves nested flat routes within a Shopify app context?

My app directory can be found in the image I've attached:

App Directory Structure

My app.jsx file:

    export default function App() {
  const { apiKey } = useLoaderData();

  return (
    <AppProvider isEmbeddedApp apiKey={apiKey}>
      <NavMenu>
        <Link to="/app" rel="home">
          Home
        </Link>
        <Link to="/app/customisers">Customisers</Link>
        <Link to="/app/additional">Additional page</Link>
      </NavMenu>
      <Outlet />
    </AppProvider>
  );
}

export function ErrorBoundary() {
  return boundary.error(useRouteError());
}

export const headers = (headersArgs) => {
  return boundary.headers(headersArgs);
};

Thank you!


Solution

  • Yes, you still need <Outlet /> even if you're not sharing components or layout. When you have a folder structure app.customisers/, Remix creates a parent-child relationship between the routes, regardless of whether you want to share components.

    Here's what happens when someone visits /app/customisers/new:

    1. Remix renders app.js (which has <Outlet />)

    2. Inside that outlet, Remix renders app.customisers/index.jsx

    3. Inside app.customisers/index.jsx 's <Outlet />, Remix renders app.customisers/new.jsx

    Without <Outlet /> in step 3, the new.jsx component has nowhere to render, causing the 404.

    You can do two things.

    1st one is:

    Add <Outlet /> to index:

    // app.customisers/index.jsx
    import { Outlet } from "@remix-run/react";
    
    export default function CustomisersIndex() {
      return <Outlet />; // Just pass through to child routes
    }
    

    2nd one is:

    Restructure to avoid nesting. Move files to:

    This creates sibling routes instead of parent-child routes, so no <Outlet /> needed.