Core Concepts
Components and Islands
Components are reusable parts of your application UI. In Radonis, components are rendered server-side by default, but can be upgraded to islands that are also hydrated client-side for interactivity.
Usage
Create your components somewhere in the resources
directory, preferably in resources/components
. Components that should be hydrated client-side must be wrapped with the island
function and placed in files ending with .island.<ext>
.
// resources/components/SomeInteractiveIsland.island.tsx
import { island } from '@microeinhundert/radonis'
function SomeInteractiveIsland({ message }: { message: string }) {
function handleClick() {
alert(message)
}
return <button type="button" onClick={handleClick}>Click me</button>
}
// The first argument should be a unique name for the island
export default island('SomeInteractiveIsland', SomeInteractiveIsland)
Note
Make sure islands don't import server-only code.
Hydrating islands
In Radonis, islands are not hydrated automatically. In order for Radonis to hydrate an island, wrap it with the HydrationRoot
component.
// resources/views/YourView.tsx
import { HydrationRoot } from '@microeinhundert/radonis'
import SomeInteractiveIsland from '../components/SomeInteractiveIsland.island.tsx'
function YourView() {
return (
<HydrationRoot>
{/* This island will be hydrated client-side */}
<SomeInteractiveIsland message="Hello world" />
</HydrationRoot>
)
}
Note
Omitting the HydrationRoot will just render the component on the server only.
Working with HydrationRoots
- HydrationRoots can only hydrate valid islands.
- HydrationRoots don't accept a component with children as direct child.
- Hydration will only take place once the HydrationRoot is in view.
- HydrationRoots only accept a single component as direct child.
Example of correct usage
<HydrationRoot>
<YourIsland />
</HydrationRoot>
Examples of incorrect usage
// Must be the direct child
<HydrationRoot>
<Suspense>
<YourIsland />
</Suspense>
</HydrationRoot>
// Can't have multiple direct children
<HydrationRoot>
<YourIsland />
<AnotherComponent />
</HydrationRoot>
// The direct child can't have children of its own
<HydrationRoot>
<YourIsland>
<AnotherComponent />
</YourIsland>
</HydrationRoot>
Hydration and Lucid
By default, data returned from your Lucid models after serialization is snake_cased. In order for data to be of the same format on both the client and the server, a custom naming strategy should be used. This naming stategy makes sure properties are kept camelCased after serialization. The following snippet shows a custom naming strategy that achieves this.
// app/Strategies/CamelCaseNamingStrategy.ts
import { string } from '@ioc:Adonis/Core/Helpers'
import { SnakeCaseNamingStrategy, BaseModel } from '@ioc:Adonis/Lucid/Orm'
export default class CamelCaseNamingStrategy extends SnakeCaseNamingStrategy {
public serializedName(_model: typeof BaseModel, propertyName: string) {
return string.camelCase(propertyName)
}
}
To use the custom naming strategy for your models, add it to each model class on the static namingStrategy
property.
// app/Models/YourModel.ts
import CamelCaseNamingStrategy from 'App/Strategies/CamelCaseNamingStrategy'
import { BaseModel } from '@ioc:Adonis/Lucid/Orm'
export default class YourModel extends BaseModel {
public static namingStrategy = new CamelCaseNamingStrategy()
}