solidjs / solid-router Goto Github PK
View Code? Open in Web Editor NEWA universal router for Solid inspired by Ember and React Router
License: MIT License
A universal router for Solid inspired by Ember and React Router
License: MIT License
<Route path="/wow/:idCamelCase" component={User} />
...
const params = useParams()
params.idCamelCase // This will always be undefined
params.idcamelcase // This works
Add support for optional params so /page
and /page/unknown
both match in this example:
<Route path="/page/:pathParam?" component={MyPage} />
Was reading some articles recently around SPA's and the issue of a document title when pages change. I thought there might be an opportunity to add something into the router config to capture a page title (potentially combined with a "template" title for building a title of site name - page name for example)
I was thinking something along the lines of having a router config property (and route component prop) of "title", which is then used during the navigation function to set the document title. The "template" could be a top level config item (or even a property of the main Router object) which sets the template string and has some sort of placeholder that is replaced with the actual title to be updated.
I'm happy to look at getting a PR together to see if it could work, but just wanted to see if it's something that would be liked/supported.
Hi.
For example, as a user I can navigate to some edit page from different routes, and after changes is applied, I need to navigate back to previous route. Is there any way to do this?
If I remember right, in react-router there was a state that can be anything (for example state: { prev: location.pathname }
). But state here is just a string or null. And looks like it doesn't work (or I don't understand something). navigate('/users/edit', { state: location.pathname })
after this, I've still got null state on edit page.
Could you add an option for empty links. Links that could be used as buttons, for example.
Then usually don't need href
. But a link must have href
, so there's a trick with javascript:void(0)
.
Input
<Link>To the moon</Link>
Output
<a href="javascript:void(0)">To the moon</a>
The idea is to set href
to javascript:void(0)
as a default value.
I guess it would be more of a discussion than an issue, but while working on the real world example rewritten with solid app router, I encountered a situation where I'd like to access the router.location and listen to the change within the data function.
I thought about just using the useRouter
and access router.location
but it turns out we can't access this context in data functions. Digging a bit into the source code, I figured you manually generate getters for query and params to be passed into the data function props.
Is there a reason we just don't feed the whole state proxy into the data function here:
Like so:
return levels[i].handler.data!(state);
I did patch it locally and it seems to work and since the proxy already exist I don't think we pay any perf price. That being said, I guess we are exposing more than the user could ever need.. And I would understand if you'd prefer discarding that issue.
It seems routing to the current page, but using a different pattern than the one you are currently on, does not update the data associated with that route. An example of when this might be done is on a product page, linking to another related product.
Steps to reproduce:
Expected result: The title reads "You are on page 2"
Actual result: The title reads "You are on page 1", even though the URLhas updated
import { createSignal, Accessor } from 'solid-js';
import { MountableElement, render } from 'solid-js/web';
import { Router, Routes, Route, Link, useData } from 'solid-app-router';
import { Params } from 'solid-app-router';
function FooData({ params }: { params: Params}): Accessor<number| undefined> {
return createSignal(parseInt(params.id))[0];
}
function Foo() {
const routingData = useData() as Accessor<number| undefined>;
return <>
<h1>You are on page {routingData()}</h1>
<Link href={`/foo/1`}>Go to page 1</Link>
<br/>
<Link href={`/foo/2`}>Go to page 2</Link>
</>;
}
render(
() => (
<Router>
<Routes>
<Route path="/foo/:id" element={<Foo />} data={FooData} />
</Routes>
</Router>
),
document.getElementById('app') as MountableElement
);
I tried to fork this repo from https://github.com/rturnq/solid-app-router-issue-58 and deployed it to test the routing.
It works when you click the links from the main index page but if you go to a specific route by changing the url it doesn't work. Sample going to this link https://solid-app-router-issue-58.vercel.app/page1
I'm currently using serve with the following config to redirect all routes back to index.html:
{
"rewrites": [
{ "source": "/**", "destination": "/index.html" },
{ "source": "/**/**", "destination": "/index.html" }
]
}
My Routes are defined as follows in Solid:
const AppRoutes = () => {
return (
<Routes>
<Route
path="/"
element={
<div>
<Link href={'/app/2'}>Link</Link>
</div>
}
/>
<Route path={'/app'}>
<Route path={'/'} element={<p>Hi</p>}></Route>
<Route path={'/*all'} element={<p>Hello</p>}></Route>
</Route>
</Routes>
);
};
Going directly in the browser to localhost:3000/app results in the correct
Hi
being displayed. Clicking the link if I go to localhost:3000 correctly displaysHello
.
However, if I decide to hit localhost:3000/app/2 in my address bar, I get the following errors:
[Error] SyntaxError: Unexpected token '<'
(anonymous function) (runtime.js:1)
[Error] SyntaxError: Unexpected token '<'
(anonymous function) (solid-js.js:1)
[Error] SyntaxError: Unexpected token '<'
(anonymous function) (regexparam.js:1)
[Error] SyntaxError: Unexpected token '<'
(anonymous function) (rturnq.js:1)
[Error] SyntaxError: Unexpected token '<'
(anonymous function) (main.js:1)
[Error] Did not parse stylesheet at 'http://localhost:3000/app/main.css' because non CSS MIME types are not allowed in strict mode.
I'm using the latest solid/solid-router and webpack
Any chance there's a known solution to this and I'm just being dumb?
I'm trying to create Facebook manual authentication flow using solid.js featuring solid-app-router. For response_type=token
, Facebook API redirect url is called having access token data in URL Fragment. More info can be found here.
How can I parse URL Fragment using solid-app-router hooks? I suppose useLocation should return location with hash as a URL Fragment, right? At least my simple code returns hash as an empty string.
For url localhost:3000/auth-callback/?#access_token=<lots_of_characters>
and the Component
import { Component } from "solid-js";
import { useLocation } from "solid-app-router";
const AuthCallback: Component = () => {
const location = useLocation();
return (
<div>{JSON.stringify(location)}</div>
);
};
export default AuthCallback;
the output is: {"pathname":"/auth-callback","search":"","hash":"","state":null,"key":"","query":{}}
The official router for Vue.js has a feature called "Named routes". It makes it possible to give the routes a separate name to refer to them instead of using the actual route. The benefit of this is that you can rename the route inside the config file but not where it has been referenced.
This feature would be a great addition.
When I use Dynamic
to switch between "internal" components in my "main page component" using a signal, <Link>
s fail with the error in the title.
(Also, on a related note, is it a bad idea to store the "internal state component" in a signal? If it is, how else am I going to create components with dynamic data (when I, for example, receive data from a websocket and want to display that data in a new component by passing it as props which is then shown using <Dynamic/>
)?)
Basically I want to know which route is currently active outside of <Routes/>
, so I can for example change nav bar styles on specific page, currently this is hard to do correctly. So my proposed solution is to allow useMatch
take RouteDefinition
object so this works:
const LIST_ROUTE = {
path: '/list',
component: List,
children: [
{ path: '/' },
{ path: '/apples' },
{ path: '/oranges' },
{ path: '/banana' },
],
}
const routes: RouteDefinition[] = [
{
path: '/',
component: Home,
},
LIST_ROUTE,
]
export const App = () => {
const Routes = useRoutes(routes)
const isListRoute = useMatch(LIST_ROUTE)
return (
<div>
<Routes />
<BottomNavBar hide={isListRoute()} />
</div>
)
}
Alternative today is to use
const isListRoute = useMatch(() => '/list/*') // Matches everything and is not a correct solution
// or way more complicated
const isListRoute = useMatch(() => '/list/')
const isListAppleRoute = useMatch(() => '/list/apple')
const isListOrangeRoute = useMatch(() => '/list/orange')
const isListBananaRoute = useMatch(() => '/list/banana')
const isListRouteActive= createMemo(() => isListRoute() && isListAppleRoute() && isListOrangeRoute() && isListBananaRoute())
There might be some other way to solve this like having <Routes />
fire change event with matched route id.
need Hashrouter
equivalent for solid.
I was trying the routing in a basic electron app and got stuck with this.
The path="/" works fine in browsers but I cannot GET that page in my electron window.
app.tsx
import { Link ,Route, Routes } from "solid-app-router";
export const App = () => {
const Nav = () => {
return (
<>
<div style={{display:"flex"}}>
<Link style={{"margin-right":"10px"}} href="/">homepage</Link>
<Link style={{"margin-right":"10px"}} href="/newcomponent">new component</Link>
</div>
</>
);
};
const Homepage = () => {
return (
<>
<Nav />
<div>homepage component.</div>
</>
);
};
const Component1 = () => {
return (
<>
<Nav />
<div>new component.</div>
</>
);
};
return (
<>
{/* <Nav /> */}
<Routes>
<Route path="/" element={<Homepage />} />
<Route path="/newcomponent" element={<Component1 />} />
</Routes>
</>
);
};
index.tsx
import { render } from "solid-js/web";
import { Router } from "solid-app-router";
import { App } from "./app.main";
render(
() => (
<Router>
<App />
</Router>
),
document.getElementById("app")
);
`
This code was supposed to show Homepage
component on loading the window.
Both components were not loaded. I have to make the Nav
component load on every page to route between pages.
The default path="/" is not getting loaded.
use - https://github.com/DhansAL/solidjs-electronForge-TS-template
Holding a modifier key such as ctrl or shift does not open links in a new browser tab or window.
lukeed/navaid
has a particularly concise but thorough implementation for this. preact-router
also has a nice and simple approach too.
Expose createPathMatcher
function. useMatch
only works with current path, so you have no options if you want for example compare previous path and see if it matches.
Allows Route
component to be extended or encapsulated. i.e. if I want an authenticated route, a good pattern might be...
type AuthenticatedRouteProp = RouteProp & { isAuthed: boolean };
const AuthenticatedRoute: Component<RouteProp> = (props) => {
return (
{ props.isAuthed ? <Route {...props} /> : <Navigate href={'/login'} /> }
);
} ;
which means that RouteProp
needs to be exposed
I was playing around with this router (and solid in general) and wanted to say thank you for this awesome library, and ecosystem you are working on. This is a breath of fresh air and I am definitely looking forward to incorporate Solid into my work!
I looked into the source code for this router and found out how to use data object. But I found out that if you are using mergeProps in the data
property in route definitions it's breaking with Proxy error, but if I just return new object (without mergeProps) it's working as intended.
I am not sure it's a solid-app-router bug or bug in the main library, but I didn't encounter this before, while using solid without this router.
Error:
'ownKeys' on proxy: trap result did not include 'Symbol(state-proxy)'
I might've misunderstood something with mergeProps so it might not be a bug at all.
I'd like to render one Route or another depending on the data from a store.
Is it supported somehow?
This one threw me off for a while until I understood how it works. In the routes definition, when a route has children, its component must contain a Route
component for those children to be rendered.
For example;
import { Route, Router } from 'solid-app-router';
import { lazy } from 'solid-js';
import { render } from "solid-js/web";
const routes = [
{
path: '/my-page',
component: lazy(() => import('./pages/my-page')),
},
{
path: '/my-page/:slug',
component: Route, // <-- this is what I was missing
children: [
{
path: '/',
component: lazy(() => import('./pages/my-page/[slug]')),
},
{
path: '/sub-page',
component: lazy(() => import('./pages/my-page/[slug]/sub-page')),
},
],
},
];
const App = () => (
<Router routes={routes}>
<Route />
</Router>
);
render(App, document.getElementById('app'));
In the example I've used Route
as the actual component but it's enough for the route's component
to contain a <Route />
.
It's obvious in retrospect but after using so many different router implementations over the years I wasn't sure if it was automagically injected or just how flexible nesting is.
Note: I know there's no formal documentation yet, so this is just a tip for when you get to it!
As discussed in Discord, the current numeric input to useData
is brittle (even with the negative-number extension of #69). Using a component at different depths of the hierarchy makes it difficult to find the right ancestor data.
The general agreement from the thread was to introduce a context-like API. I believe the plan is to pass in the same function that was given as a data
attribute to the Route
. For example, <Route data={UserData}>
woudl get accessed like so:
const data = useData(UserData);
Type-wise, this could work as follows:
function useData<T>(dataFunc: () => T): T | undefined;
// returns undefined if no ancestor route uses this dataFunc
There was also discussion about useDataLoader
but that seemed more specific to Resource
s (maybe a Solid Start thing?).
A related question is whether this should replace the existing numeric model (breaking change), or augment it as a second option (more code). I think at least we should preserve the common case, where useData()
returns the current route's data, though this version will be better for typing even in that case.
It looks like the syntax supported for path patterns is the same as React Router, but it'd be helpful to at least say "supports :param
and *rest
parameters", and perhaps for those unfamiliar, clarify that :param
matches a single component between slashes, while *
must be the final thing (and looking at the code, I think prefixed by a slash?).
I found this hard to figure out without reading the code, because you also mention Ember Router which I'm not familiar with, so I figured there might be some additional pattern syntax.
Admittedly I couldn't find this well documented in React Router either, but I found brief mention here and here.
I am not sure if this is a bug or just me not understanding how routes priority works.
Reproduction
<Route path="/" component={User}>
<Route path="/users" element={<div>Should match this</div>} />
</Route>
<Route path="/users" element={<div>But matches this</div>} />
I discovered this while trying to use <Navigate>
and redirect from /|/users
to /users/default-route
at routing.js:231
at untrack (solid.js:401)
at navigateFromRoute (routing.js:192)
at routing.js:246
at Object.fn (RouteGuard.tsx:52)
at runComputation (solid.js:593)
at updateComputation (solid.js:578)
at runTop (solid.js:654)
at runUserEffects (solid.js:753)
at solid.js:714
RouteGaurd line 52 is just a navigate('/login')
I am using the latest solid-app-router and solid (v1.3.1)
Which points to https://github.com/solidjs/solid-app-router/blob/ea0edc85502225ab62085014e9fb309df351634f/src/routing.ts#L338
//From dev tools
if (resolvedTo !== current || nextState !== state()) {
if (isServer) {
if (output) {
output.url = resolvedTo;
}
setSource({ value: resolvedTo, replace, scroll, state: nextState });
}
else {
const len = referrers.push({ value: current, replace, scroll, state });
start(() => {
setReference(resolvedTo);
setState(nextState);
}).then(() => { // Here is the error
if (referrers.length === len) {
navigateEnd({
value: resolvedTo,
state: nextState
});
}
});
}
}
It would be interesting to have a feature like this:
<Router autowire={true} root={process.env.PUBLIC_URL}>
<Nav />
<Route class="view" />
</Router>
Which takes a predefined (hard-coded) folder, let's say "pages" (since I've seen it on every solid projects. It will parse this folder and get path.jsx/tsx
with its path.data.js/ts
and generate the routes
Trying to setup ssr with vite. Created server.ts as described in solid/vite docs/examples but at
render = (await vite.ssrLoadModule('/src/entry-server.tsx')).render; // renderToStringAsync inside
got error:
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /home/rootfs/var/www/thesite/thesite/node_modules/solid-app-router/dist/index.js
require() of ES modules is not supported.
require() of /home/rootfs/var/www/thesite/thesite/node_modules/solid-app-router/dist/index.js from /home/rootfs/var/www/thesite/thesite/node_modules/vite/dist/node/chunks/dep-11db14da.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /home/rootfs/var/www/thesite/thesite/node_modules/solid-app-router/package.json.
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1089:13)
at Module.load (internal/modules/cjs/loader.js:937:32)
at Function.Module._load (internal/modules/cjs/loader.js:778:12)
at Module.require (internal/modules/cjs/loader.js:961:19)
at require (internal/modules/cjs/helpers.js:92:18)
at nodeRequire (/home/rootfs/var/www/thesite/thesite/node_modules/vite/dist/node/chunks/dep-11db14da.js:73337:17)
at ssrImport (/home/rootfs/var/www/thesite/thesite/node_modules/vite/dist/node/chunks/dep-11db14da.js:73290:20)
at eval (/src/AppDocument.tsx:18:31)
at instantiateModule (/home/rootfs/var/www/thesite/thesite/node_modules/vite/dist/node/chunks/dep-11db14da.js:73323:166)
Could you help with it?
Thank you!
When I run my app in development mode (with vite-plugin-solid
) I run into an issue where normalize
function (found in utils.js
) receives undefined
as parameter. (This happens before path becomes /
or any other string)
The code in normalize function doesn't check if path is a string and is trying to use replace method which causes runtime error.
I don't see this error when I serve a built application, so it might be related to solid-refresh
Here's my App
with the routing tree:
import { Suspense, lazy } from 'solid-js'
import { Router, Routes, Route } from 'solid-app-router'
import AppHeader from './components/AppHeader'
import AppFooter from './components/AppFooter'
import './app.css'
const Home = lazy(() => import('./screens/home/Home'))
const ScreenOne = lazy(() => import('./screens/screen-one/ScreenOne'))
const ViewOne = lazy(() => import('./screens/screen-one/ViewOne'))
const ViewTwo = lazy(() => import('./screens/screen-one/ViewTwo'))
const ViewThree = lazy(() => import('./screens/screen-one/ViewThree'))
const ScreenTwo = lazy(() => import('./screens/screen-two/ScreenTwo'))
const ScreenThree = lazy(() => import('./screens/screen-three/ScreenThree'))
const NotFound = () => {
return (
<main>
<h2>404</h2>
<p>Path not found.</p>
</main>
)
}
const App = () => {
return (
<div class="app">
<Router>
<AppHeader />
<Suspense fallback="Loading...">
<Routes>
<Route path="/screen-one" element={ <ScreenOne /> }>
<Route path="/view-one" element={ <ViewOne /> } />
<Route path="/view-two" element={ <ViewTwo /> } />
<Route path="/view-three" element={ <ViewThree /> } />
<Route path="/" element={ <ViewOne /> } />
</Route>
<Route path="/screen-two" element={ <ScreenTwo /> } />
<Route path="/screen-three" element={ <ScreenThree /> } />
<Route path="/" element={ <Home /> } />
<Route path="/*all" element={ <NotFound /> } />
</Routes>
</Suspense>
<AppFooter />
</Router>
</div>
)
}
export default App
AppHeader
component contains Link
components:
import { Link } from 'solid-app-router'
const AppHeader = () => {
return (
<header class="app-header">
<h1><Link href="/">Home</Link></h1>
<nav class="app-navigation">
<ul>
<li><Link href="/screen-one">Screen One</Link></li>
<li><Link href="/screen-two">Screen Two</Link></li>
<li><Link href="/screen-three">Screen Three</Link></li>
</ul>
</nav>
</header>
)
}
export default AppHeader
The path changes instantly but the Route component is still fetching, because of that, a flash of the blank page occurs.
How can I achieve a blocking way of fetching the Route Component, so when navigating to another lazy page it doesn't show a blank page but stays on the current one, and maybe shows a progress bar/line like on youtube by using a "hook" useIsFetching(route?)
, I Looked into Data functions but I think it doesn't suit this use case because I just want to load a Route Component and no data alongside it.
Example https://codesandbox.io/s/solid-router-185j1
function App() {
const [base, setBase] = createSignal('old');
return <Router base={base()}>
<button onclick={() => setBase('new')}>Change base</button>
<Link href="/about">about</Link>
<Routes />
</Router>
}
After change base
to new
then call useNavigator()('/about')
still navigate to /old/about
Common use case is to show completely empty not found page, if route doesn't match, without any specific sub path elements, currently this is not possible without redirects which change current path or some position absolute styling hacks.
In this example I wish to show top level Not Found page and not sub level one when no other routes match.
Example
const User = () => (
<div class="user-page">
<div>User page</div>
<Outlet />
</div>
);
...
<Routes>
<Route path="/" element={<div>Home</div>} />
<Route path="/users" component={User}>
<Route path="/" element={<div>User</div>} />
<Route path="/sub" element={<div>UserSub</div>} />
<Route
path="/*all"
element={
<div>
Foward this to top level "Not Found" page somehow
while keeping current path.
</div>
}
/>
</Route>
<Route path="/*all" element={<div>Not Found</div>} />
</Routes>;
Clicking on a <Link href="..." />
and changing routes does not scroll to the top of the page, much like a regular link on a normal multi-page website.
As a reference, sapper
for example, will scrollTo
top on new routes and also maintains a history of previous scroll position in case the user goes back/forward. vue-router
seems to do the same thing.
Possibly similar to #6 - using mergeProps
in data seems to cause the following error on latest solid-js and solid-app-router:
TypeError
can't define property Symbol("state-node"): Object is not extensible
getDataNodes
https://uojg1.csb.app/node_modules/solid-js/dist/solid.js:1131:23
get
https://uojg1.csb.app/node_modules/solid-js/dist/solid.js:1158:33
Data/</<
tab1.tsx:12:18
9 | <button type="button" onClick={increment}>
10 | {count()}
11 | </button>
> 12 | <code>{JSON.stringify(props, null, 2)}</code>
| ^
13 | </>
14 | );
15 | }
Here is the forked sandbox from #6 where I just updated the versions to latest solid-js
and solid-app-router
: https://codesandbox.io/s/silent-voice-uojg1
Removing the use of mergeProps
allows it to work without error.
I am trying out solidjs
with solid-start
. Here's the repo: https://github.com/HriBB/solid-started
I have two pages:
Homepage
// src/pages/index.data.ts
import { createResource } from 'solid-js'
export type Data = {
page: string
simulateFetch: boolean
sleep: number
}
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
const simulateFetch = async () => {
console.log('fetch home data')
const ms = 1000
await sleep(ms)
return {
page: 'home',
simulateFetch: true,
sleep: ms,
}
}
export default function HomeData() {
const [data] = createResource(simulateFetch)
return data
}
// src/pages/index.tsx
import { Resource } from 'solid-js'
import { useData } from 'solid-app-router'
import { Data } from './index.data'
import { Hero } from '~/components/home/hero'
import { CallToAction } from '~/components/home/call-to-action'
import { GettingStarted } from '~/components/home/getting-started'
import { LoremIpsum } from '~/components/home/lorem-ipsum'
import { Pricing } from '~/components/home/pricing'
export default function Home() {
const data = useData<Resource<Data>>()
return (
<div class="leading-normal tracking-normal text-white gradient">
<div text="center" p="t-20">
{JSON.stringify(data(), null, 2)}
</div>
<Hero />
<LoremIpsum />
<GettingStarted />
<Pricing />
<CallToAction />
</div>
)
}
Blog
// src/pages/blog.data.ts
import { createResource } from 'solid-js'
export type Data = {
page: string
simulateFetch: boolean
sleep: number
}
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
const simulateFetch = async () => {
console.log('fetch blog data')
const ms = 1000
await sleep(ms)
return {
page: 'blog',
simulateFetch: true,
sleep: ms,
}
}
export default function BlogData() {
const [data] = createResource(simulateFetch)
return data
}
// src/pages/blog.tsx
import { Resource } from 'solid-js'
import { useData } from 'solid-app-router'
import { PostList } from '~/components/post'
import { Content } from '~/components/ui'
import { Data } from './blog.data'
export default function Blog() {
const data = useData<Resource<Data>>()
return (
<Content>
{JSON.stringify(data(), null, 2)}
</Content>
)
}
When I navigate back and forth between home and blog, simulateFetch
function is execute multiple times. Here's the console.log:
client.ts:22 [vite] connecting...
client.ts:52 [vite] connected.
index.data.ts:12 fetch home data
blog.data.ts:12 fetch blog data
blog.data.ts:12 fetch blog data
blog.data.ts:12 fetch blog data
index.data.ts:12 fetch home data
index.data.ts:12 fetch home data
blog.data.ts:12 fetch blog data
blog.data.ts:12 fetch blog data
index.data.ts:12 fetch home data
index.data.ts:12 fetch home data
...
Is this a bug or am I doing something wrong?
It seems can not use more than 2 levels of nesting.
Is it a bug or design limit?
https://codesandbox.io/s/solid-router-nesting-thlvg?file=/src/main.tsx
import { Router, Route, Routes, Link, useParams, Outlet} from 'solid-app-router'
import { render } from 'solid-js/web'
function AppFrame() {
return (
<>
<h1>AppFrame</h1>
<Outlet />
</>
)
}
function ArticleList() {
return (
<>
<h2>Article List</h2>
<ol>
<li>
<Link href="/articles/1">Article #1</Link>
</li>
<li>
<Link href="/articles/2">Article #2</Link>
</li>
<li>
<Link href="/articles/3">Article #3</Link>
</li>
</ol>
<Outlet />
</>
)
}
function Article() {
const params = useParams<{ id: string }>()
return <p>Article Content: {params.id}</p>
}
export default function App() {
return (
<Router>
<p>
<Link href="/">Home</Link>
</p>
<hr />
<Routes>
<Route path="/" element={<AppFrame />}>
<Route path="/" element={<ArticleList />}>
<Route path="/articles/:id" element={<Article />} />
</Route>
</Route>
</Routes>
</Router>
)
}
render(() => <App />, document.getElementById('app'))
Hi,
When im in development, button is navigating to another url.
but when i run npm run build
and i do npm run serve
and i click the button to navigate its not working only the url is change.
TIA
Summary: The active class triggers for NavLink
s using the /
path, regardless of what path you visit.
Steps to reproduce:
Expected result: Only the list NavLink
has an active
class
Actual result: Boththe NavLink
s have an active
class
import { MountableElement, render } from "solid-js/web";
import { Router, useRoutes, NavLink } from "solid-app-router";
const routes = [
{
path: "/",
component: () => <div>Home</div>
},
{
path: "/list",
component: () => <div>List</div>,
},
];
function App() {
const Routes = useRoutes(routes);
return (
<>
<NavLink href="/">
Home
</NavLink>
<NavLink href="/list">
List
</NavLink>
<Routes />
</>
);
}
render(
() => (
<Router>
<App />
</Router>
),
document.getElementById("app") as MountableElement
);
Commentary
I get the logic behind this, in that if you are visiting product/<some id here>
you might want to match against a /product
NavLink
. But I think there still should be a way to get an active class on your root directory without it always having to be active.
Also, thanks for all the hard work you guys have done on this router, and solidjs in general!
The parseQuery
in the example for useLocation
is a bit misleading IMHO. Mention that location.query
returns a Proxy
that can be used to access the query parameters, e.g.
const location = useLocation();
return <p>{location.query.code}</p>;
Also mention whether query parameters are downcased or not, so whether ?confirmationCode=123
has to be accessed as location.query.confirmationcode
or location.query.confirmationCode
.
Suspense & Solid app router together will break the suspense fallback if you click on a link where a resource is loading.
The page will be blocked instead until the resource is done loading.
I'm guessing this isn't expected behavior.
Example:
//App.js
export default () => {
const Routes = useRoutes(routes);
<Suspense fallback={<p>Loading Page...</p>}>
<Routes />
</Suspense>
};
//Dashboard.js
export default () => {
const [data] = createResource(() => {
return new Promise((resolve) => {
setTimeout(() => {
resolve('DATA LOADED');
}, 3000);
});
});
return <div>Dashboard Page {data()}</div>;
};
Probably missing adding the root in a key place.
When call the useNavigate
function, useRouter()
cannot find a router context.
Uncaught Error: Make sure your app is wrapped in a <Router />
arises, but the app is certainly wrapped in a Router component.
Here is some of the code:
// index.tsx
import { render } from 'solid-js/web'
import { Router, useRoutes } from 'solid-app-router'
import { Component, lazy } from 'solid-js'
const routes = [
{
path: '/',
component: lazy(() => import('./pages/Home')),
children: [
{
path: '/',
component: lazy(() => import('./pages/scripts/ScriptList')),
},
{
path: '/script/add',
component: lazy(() => import('./pages/scripts/ScriptAdd')),
},
],
},
]
const App: Component = () => {
const Routes = useRoutes(routes)
return <Routes />
}
render(
() => (
<Router>
<App />
</Router>
),
document.getElementById('root')
)
// other.tsx
import { useNavigate } from 'solid-app-router'
const fn = () => {
const navigate = useNavigate()
navigate('/script/add')
}
Is anything wrong in this?
<Link
href="/about"
classList={{
"bg-blue-100 hover:bg-blue-100": Boolean(useMatch(() => "/about")()),
}}
className="rounded-full hover:bg-gray-100 px-2 py-1"
>
<span className="text-sm font-bold">About Us</span>
</Link>
Refresh on /about path do not apply classList
Add replace option to link element so path is is replaced instead of pushed when clicked.
<Link href='/path' replace />
React router seems to support it.
Hello I have some problem when directly change the URL in URL bar or refresh page and it got "404 Not Found" but when I have changed URL by clicking on <Link>
then it show the component normally
It's only affected to sub-route such as "/users", "/2", "/1" but the route "/" is perfectly fine.
My project environment
*Edit
This is my snowpack config file
* @type {import('snowpack').SnowpackConfig}
*/
const config = {
mount: {
public: "/",
src: "/assets"
},
packageOptions: {
installTypes: true,
NODE_ENV: true
},
devOptions: {
out: "dist",
open: "none",
bundle: true
},
buildOptions: {
clean: true,
out: "dist"
},
plugins: ["@snowpack/plugin-typescript", "@snowpack/plugin-babel", "@snowpack/plugin-postcss"],
optimize: {
bundle: true,
minify: true,
target: "es2020",
treeshake: true,
splitting: true
}
};
module.exports = config;
Hi all, I apologize in advance for the translation.
I am trying to start writing in this wonderful framework. I have set up a project build for server side rendering. Everything works fine for me. But I decided to use this router to move forward. Client-side works fine, but after connecting the library, server-side stopped building.
ERROR in ./node_modules/solid-app-router/dist/index.js 503:15-23
export 'template' (imported as 'template') was not found in 'solid-js/web' (possible exports: Assets, Dynamic, ErrorBoundary, For, HydrationScript, Index, Match, NoHydration, Portal, Show, Suspense, SuspenseList, Switch, createComponent, escape, generateHydrationScript, getHydrationKey, isServer, mergeProps, pipeToNodeWritable, pipeToWritable, renderToString, renderToStringAsync, resolveSSRNode, spread, ssr, ssrBoolean, ssrClassList, ssrHydrationKey, ssrSpread, ssrStyle)
@ ./src/shared/App.tsx 14:0-42 79:27-33
@ ./src/server/index.tsx 10:0-35 34:39-42
ERROR in ./node_modules/solid-app-router/dist/index.js 652:4-10
export 'insert' (imported as 'insert') was not found in 'solid-js/web' (possible exports: Assets, Dynamic, ErrorBoundary, For, HydrationScript, Index, Match, NoHydration, Portal, Show, Suspense, SuspenseList, Switch, createComponent, escape, generateHydrationScript, getHydrationKey, isServer, mergeProps, pipeToNodeWritable, pipeToWritable, renderToString, renderToStringAsync, resolveSSRNode, spread, ssr, ssrBoolean, ssrClassList, ssrHydrationKey, ssrSpread, ssrStyle)
@ ./src/shared/App.tsx 14:0-42 79:27-33
@ ./src/server/index.tsx 10:0-35 34:39-42
ERROR in ./node_modules/solid-app-router/dist/index.js 654:4-10
export 'effect' (imported as 'effect') was not found in 'solid-js/web' (possible exports: Assets, Dynamic, ErrorBoundary, For, HydrationScript, Index, Match, NoHydration, Portal, Show, Suspense, SuspenseList, Switch, createComponent, escape, generateHydrationScript, getHydrationKey, isServer, mergeProps, pipeToNodeWritable, pipeToWritable, renderToString, renderToStringAsync, resolveSSRNode, spread, ssr, ssrBoolean, ssrClassList, ssrHydrationKey, ssrSpread, ssrStyle)
@ ./src/shared/App.tsx 14:0-42 79:27-33
@ ./src/server/index.tsx 10:0-35 34:39-42
ERROR in ./node_modules/solid-app-router/dist/index.js 654:17-29
export 'setAttribute' (imported as 'setAttribute') was not found in 'solid-js/web' (possible exports: Assets, Dynamic, ErrorBoundary, For, HydrationScript, Index, Match, NoHydration, Portal, Show, Suspense, SuspenseList, Switch, createComponent, escape, generateHydrationScript, getHydrationKey, isServer, mergeProps, pipeToNodeWritable, pipeToWritable, renderToString, renderToStringAsync, resolveSSRNode, spread, ssr, ssrBoolean, ssrClassList, ssrHydrationKey, ssrSpread, ssrStyle)
@ ./src/shared/App.tsx 14:0-42 79:27-33
@ ./src/server/index.tsx 10:0-35 34:39-42
ERROR in ./node_modules/solid-app-router/dist/index.js 720:0-14
export 'delegateEvents' (imported as 'delegateEvents') was not found in 'solid-js/web' (possible exports: Assets, Dynamic, ErrorBoundary, For, HydrationScript, Index, Match, NoHydration, Portal, Show, Suspense, SuspenseList, Switch, createComponent, escape, generateHydrationScript, getHydrationKey, isServer, mergeProps, pipeToNodeWritable, pipeToWritable, renderToString, renderToStringAsync, resolveSSRNode, spread, ssr, ssrBoolean, ssrClassList, ssrHydrationKey, ssrSpread, ssrStyle)
@ ./src/shared/App.tsx 14:0-42 79:27-33
@ ./src/server/index.tsx 10:0-35 34:39-42
It seems the JSX example in the README contains the useRoutes hook when it shouldn't be needed for it and will cause confusion for incoming new users :)
...
import { Router, Routes, Route, Link } from "solid-app-router";
...
function App() {
const Routes = useRoutes(routes); <--- To remove
return (
<>
<h1>Awesome Site</h1>
<Link class="nav" href="/">
Home
</Link>
<Link class="nav" href="/users">
Users
</Link>
<Routes>
<Route path="/users" element={<Users />} />
<Route path="/users/:id" element={<User />}>
<Route path="/" element={<UserHome />} />
<Route path="/settings" element={<UserSettings />} />
<Route path="/*all" element={<UserNotFound />} />
</Route>
<Route path="/" element={<Home />} />
<Route path="/*all" element={<NotFound />} />
</Routes>
</>
);
}
Take for example the route "/users/:id/orders?order=popular".
How does one read the following inside the component which is routed to?:
I've been trying to read through the source code and at first glance it looks like these ought to be set as props on the component, but it's not proving to a very productive exercise.
Would you be able to provide a little more documentation regarding the basic usage?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.