Skip to content

Solid Router

Bindings for @solidjs/router. They live in the Partas.Solid.Router namespace, which ships inside the Partas.Solid package.

open Partas.Solid
open Partas.Solid.Router

Partas.Solid does not install the npm package:

npm install @solidjs/router

An example

[<SolidComponent>]
let Home () = h1 () { "Home" }

[<SolidComponent>]
let About () = h1 () { "About" }

[<Erase>]
type Layout() =
    inherit div()

    [<SolidTypeComponent>]
    member props.View =
        div () {
            nav () {
                A(href = "/") { "Home" }
                A(href = "/about", activeClass = "active") { "About" }
            }
            props.children
        }

[<SolidComponent>]
let App () =
    Router(root = !@Layout) {
        Route(path = "/", component' = !@Home)
        Route(path = "/about", component' = !@About)
    }

A Route can hold nested Routes. component' and root take a TagValue: use !@Component to pass a component rather than call it. The router passes the matched route to root as children, which is why Layout is a type component that inherits div. Like every [<SolidTypeComponent>], Layout must be declared in a namespace or module whose name starts with Partas.Solid (see SolidTypeComponent).

What the plugin emits for Router () { Route () }:

import { Route, Router } from "@solidjs/router";

<Router>
    <Route />
</Router>

Components

Router

type Router() = interface HtmlElement
PropertyType
rootTagValue
base'string
actionBasestring
preloadbool
explicitLinksbool
urlstring

The body accepts Route children, or a RootConfig[] for config-based routing.

HashRouter

type HashRouter() = inherit Router()

Same properties as Router.

MemoryRouter

type MemoryRouter() = inherit Router()

Adds history: MemoryHistory. Create one with createMemoryHistory ().

MemoryHistory

member get(): string
member set(value: string, ?scroll: bool, ?replace: bool): unit   // emitted as one options object
member back()
member forward()
member go(n: int): unit
member listen(listener: string -> unit): unit -> unit

listen returns the function that removes the listener.

Route

type Route() = interface HtmlElement
PropertyTypeNotes
pathstring
pathsstring arraySets path to an array of paths.
component'TagValue
matchFiltersobj
preloadRoutePreloadFunc

RootConfig

[<Pojo>]
type RootConfig(path: string, ``component``: HtmlElement)

component' is an alias for ``component``.

A

type A() = interface RegularNode
PropertyType
hrefstring
noScrollbool
replacebool
stateobj
activeClassstring
inactiveClassstring
end'bool
type Navigate() = interface RegularNode

Properties: href: string, state: obj.

useNavigate

useNavigate(): Navigator

Navigator has three ways to call it:

abstract Invoke: ``to``: string * ?options: NavigateOptions -> unit
abstract InvokeOptions: ``to``: string * ?resolve: bool * ?replace: bool * ?scroll: bool * ?state: obj -> unit
abstract Invoke: delta: float -> unit

InvokeOptions builds the options object for you, so you do not need a NavigateOptions:

let navigate = useNavigate ()
navigate.InvokeOptions ("/login", replace = true)
navigate.Invoke(-1.)

NavigateOptions is a [<Pojo>] with the optional arguments resolve, replace, scroll and state.

useLocation

useLocation(): Location
type Path =
    abstract pathname: string
    abstract search: string
    abstract hash: string

type Location =
    inherit Path
    abstract query: obj
    abstract state: obj option
    abstract key: string

useParams

useParams(): obj

useSearchParams

useSearchParams(): Signal<obj>

See Solid-js for Signal<'T>.

useIsRouting

useIsRouting(): unit -> bool

useMatch

useMatch(fn: unit -> string, ?matchFilters: obj): unit -> bool

useBeforeLeave

useBeforeLeave(listener: BeforeLeaveEventArgs -> unit): unit
type BeforeLeaveEventArgs =
    abstract from: Location
    abstract ``to``: U2<string, float>
    abstract options: NavigateOptions option
    abstract defaultPrevented: bool
    abstract preventDefault: unit -> unit
    abstract retry: ?force: bool -> unit

useCurrentMatches

useCurrentMatches(): unit -> RouteMatch[]
type PathMatch =
    abstract ``params``: obj
    abstract path: string

type RouteMatch =
    inherit PathMatch
    abstract route: RouteDescription

type RouteDescription =
    abstract key: obj
    abstract originalPath: string
    abstract pattern: string
    abstract preload: RoutePreloadFunc option
    abstract matcher: (string -> PathMatch option)
    abstract matchFilters: obj option
    abstract info: obj option

usePreloadRoute

usePreloadRoute(): (string -> PreloadData) -> unit

PreloadData is a [<Pojo>] with one field, preloadData: bool.

Preloading

type RoutePreloadFunc = RoutePreloadFuncArgs -> unit

type RoutePreloadFuncArgs =
    abstract ``params``: obj
    abstract location: Location
    abstract intent: Intent

Intent is a [<StringEnum>] with [<RequireQualifiedAccess>]: Intent.Initial, Intent.Native, Intent.Navigate, Intent.Preload.

Data

query

query<'Input, 'Output>(fn: 'Input -> 'Output, ?name: string): 'Input -> 'Output
query'<'Input, 'Output>(fn: 'Input -> 'Output, ?name: string): Query<'Input, 'Output>

query hands back a plain F# function. query' hands back a Query, which exposes the cache key:

type Query<'Input, 'Output> =
    abstract Invoke: 'Input -> 'Output
    abstract key: string
    abstract keyFor: 'Input -> string

If you used query, the extension members .key and .keyFor value on the returned function do the same thing.

revalidate

revalidate(key: string, ?force: bool): unit

createAsync

createAsync<'T>(
    fn: 'T option -> Promise<'T>,
    ?name: string,
    ?initialValue: 'T,
    ?deferStream: bool,
    ?onHydrated: unit -> unit,
    ?ssrLoadFrom: string,
    ?storage: unit -> Signal<'T>
    ): Accessor<'T>

createAsyncWithLatest takes the same arguments and returns an AsyncAccessor<'T>, which adds latest: 'T. On a plain accessor the .latest extension member reads the same field.

createAsyncStore

createAsyncStore<'T>(
    fn: 'T option -> Promise<'T>,
    ?name: string,
    ?initialValue: 'T,
    ?deferStream: bool,
    ?reconcile: obj,
    ?onHydrated: unit -> unit,
    ?ssrLoadFrom: string
    ): Accessor<'T>

createAsyncStoreWithLatest is the AsyncAccessor<'T> form.

Actions

action

Actions only work with POST requests.

action<'Input, 'Output>(
    handler: 'Input -> Promise<'Output>,
    ?name: string,
    ?onComplete: Submission<'Input, 'Output> -> unit
    ): SolidAction<'Input, 'Output>

name and onComplete are passed as one options object.

type SolidAction<'Input, 'Result> =
    abstract url: string
    abstract with': 'Input -> string

useAction

useAction<'Input, 'Result>(action: SolidAction<'Input, 'Result>): 'Input -> 'Result

This avoids FormData, but needs client-side JavaScript, so it is not progressively enhanced the way a form is.

useSubmission, useSubmissions

useSubmission<'Input>(action: SolidAction<'Input, unit>, ?filter: 'Input -> bool): Submission<'Input, unit>
useSubmissions<'Input>(action: SolidAction<'Input, unit>, ?filter: 'Input -> bool): Submission<'Input, unit>[]
type Submission<'Input, 'Result> =
    abstract input: 'Input
    abstract result: 'Result option
    abstract error: obj
    abstract pending: bool
    abstract url: string
    abstract clear: (unit -> unit)
    abstract retry: (unit -> unit)

Do not use the .pending extension member on an array of submissions: it has no implementation and always returns undefined. Use Array.exists _.pending.

Not bound

preload, json, redirect and reload are not bound. Import them with Fable.Core.JsInterop.import.

Edit this page