Module: framework¶
React framework for decentralized apps.
Purpose¶
The framework
module is the highest-level abstraction provided by the Self.ID SDK, aimed at
helping developers to quickly get started with building decentralized apps using Ceramic with
React.
The framework is built on top of the core
, web
and react
modules to provide APIs to easily authenticate users, keep track of the current user and
interact with both public (read-only) and user-owned (mutable) records.
Installation¶
npm install @self.id/framework
Common use-cases¶
Configure the Provider¶
The Provider
component must be added at the root of the
application tree in order to use the hooks described below. It can be used to provide a custom
configuration for the Self.ID clients, authentication, state and UI options.
import { Provider } from '@self.id/framework'
function App({ children }) {
return <Provider client={{ ceramic: 'testnet-clay' }}>{children}</Provider>
}
Authenticate the user¶
The framework provides a React hook to easily initiate an authentication flow for the Viewer (the "current user" of the app). This flow is made of the following steps:
The user authentication flow consists of the following steps:
- An Ethereum authentication provider is created using the Ethereum provider.
- The auth flow with 3ID Connect starts, using the Ethereum authentication provider.
- A
SelfID
instance is created and stored in application state.
Once this flow is successfully applied, the Viewer's cookie is set to the authenticated DID and writing records associated to the Viewer becomes possible.
import { EthereumAuthProvider, useViewerConnection } from '@self.id/framework'
// A simple button to initiate the connection flow. A Provider must be present at a higher level
// in the component tree for the `useViewerConnection()` hook to work.
function ConnectButton() {
const [connection, connect, disconnect] = useViewerConnection()
return connection.status === 'connected' ? (
<button
onClick={() => {
disconnect()
}}>
Disconnect ({connection.selfID.id})
</button>
) : 'ethereum' in window ? (
<button
disabled={connection.status === 'connecting'}
onClick={async () => {
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts',
})
await connect(new EthereumAuthProvider(window.ethereum, accounts[0]))
}}>
Connect
</button>
) : (
<p>
An injected Ethereum provider such as{' '}
<a href="https://metamask.io/">MetaMask</a> is needed to authenticate.
</p>
)
}
Auth Session Management¶
Reference did-session for more examples of managing the session for a user. Following code expands on example above.
// ...
const [connection, connect, disconnect] = useViewerConnection()
// ...
// get session string you serialized and stored before, check if still valid (or how much longer)
const sessionStr = ...
const selfid = await connect(new EthereumAuthProvider(window.ethereum, accounts[0]), sessionStr)
// ...
// get session to serialize and store
const session = selfid.client.session //or connection.selfID.client.session
session.serialize()
// ...
Interact with a viewer record¶
The useViewerRecord
hook loads the record for a given
definition in the index of the current viewer, with the following variants:
- If no viewer is set, no record can be loaded
- If the viewer is not authenticated, the record gets loaded but cannot be mutated
- If the viewer is authenticated, the record gets loaded and be mutated
import { useViewerRecord } from '@self.id/framework'
// Load and display the record contents
function ShowViewerName() {
const record = useViewerRecord('basicProfile')
const text = record.isLoading
? 'Loading...'
: record.content
? `Hello ${record.content.name || 'stranger'}`
: 'No profile to load'
return <p>{text}</p>
}
// Mutate the record
function SetViewerName() {
const record = useViewerRecord('basicProfile')
return (
<button
disabled={!record.isMutable || record.isMutating}
onClick={async () => {
await record.merge({ name: 'Alice' })
}}>
Set name
</button>
)
}
Read a public record¶
The usePublicRecord
hook is similar to the
useViewerRecord
hook described above, but reading from the index of an explicitly provided
account rather than the viewer. Public records are read-only, useViewerRecord
must be used in
case mutations are needed.
import { usePublicRecord } from '@self.id/framework'
function ShowProfileName({ did }) {
const record = usePublicRecord('basicProfile', did)
const text = record.isLoading
? 'Loading...'
: record.content
? `Hello ${record.content.name || 'stranger'}`
: 'No profile to load'
return <p>{text}</p>
}
Upgrading from 0.3.x to 0.4.x¶
Version 0.4.x
switched the default authentication method and libray from 3id-connect with 3ID DIDs to did-session with PKH DIDs. If you wish to upgrade and still use 3id-connect you can pass a flag and configure your provider as follows. There are no other changes in v0.4.x
, making upgrading not required at the moment if you dont wish too change auth methods, but PKH DIDs will be the recommended account going forward.
import { Provider } from '@self.id/framework'
function App({ children }) {
return <Provider client={{ ceramic: 'testnet-clay' }} threeidConnect={true}>{children}</Provider>
}
**Switching authentication methods with out consideration will change DIDs for users and result in any prior data not being resolved. **
Server-side prefetching¶
Server-side rendering can be used to improve the user experience for the first load of an app or
page. The framework exports a RequestClient
class from the
@self.id/react
package that can be used to fetch wanted records on the server
in order to have them immediately available by the usePublicRecord
and useViewerRecord
hooks.
The following example shows how this can be used in a Next.js page, using
the ShowViewerName
component created in the previous example:
import { Provider, RequestClient } from '@self.id/framework'
export const getServerSideProps = async (ctx) => {
const client = new RequestClient({
ceramic: 'testnet-clay',
// Inject the cookie from the request headers to parse the viewerID
cookie: ctx.req.headers.cookie,
})
if (client.viewerID != null) {
// If the viewerID is set, fetch its profile
await client.prefetch('basicProfile', client.viewerID)
}
return { props: { state: client.getState() } }
}
// Use the state prop injected by the server
export default function Home({ state }) {
return (
<Provider state={state}>
<ShowViewerName />
</Provider>
)
}
Re-exported classes¶
core.Core
core.PublicID
react.ReactClient
react.RequestClient
web.SelfID
EthereumAuthProvider
from 3ID Connect
Type aliases¶
ConnectNetwork¶
Ƭ ConnectNetwork: "dev-unstable"
| "mainnet"
| "testnet-clay"
Ceramic networks supported by 3ID Connect.
Dimensions¶
Ƭ Dimensions: Object
Type declaration¶
Name | Type |
---|---|
height |
number |
width |
number |
ProviderProps¶
Ƭ ProviderProps<ModelTypes
>: Object
Type parameters¶
Name | Type |
---|---|
ModelTypes |
extends ModelTypeAliases = CoreModelTypes |
Type declaration¶
Name | Type | Description |
---|---|---|
children |
ReactNode |
- |
client? |
ReactClient <ModelTypes > | WebClientParams <ModelTypes > |
An instance of ReactClient or client configuration parameters . |
queryOptions? |
QueryObserverOptions |
Custom options for the internal react-query configuration. |
state? |
RequestState |
RequestState emitted by a RequestClient instance. |
PublicRecord¶
Ƭ PublicRecord<ContentType
>: Object
A PublicRecord provides an interface for interacting with record stored on Ceramic, associated to a given DID string.
Type parameters¶
Name |
---|
ContentType |
Type declaration¶
Name | Type | Description |
---|---|---|
content? |
ContentType |
Record contents, if loaded. |
error? |
unknown |
Possible error raised when attempting to load the record. |
isError |
boolean |
true when the record failed to load, false otherwise. |
isLoading |
boolean |
true when the record is being loaded, false otherwise. |
RequestClientParams¶
Ƭ RequestClientParams<ModelTypes
>: CoreParams
<ModelTypes
> & { cookie?
: string
}
Type parameters¶
Name | Type |
---|---|
ModelTypes |
extends ModelTypeAliases = CoreModelTypes |
RequestState¶
Ƭ RequestState: Object
Type declaration¶
Name | Type | Description |
---|---|---|
hydrate? |
DehydratedState |
Serialized records to hydrate. |
viewerID? |
string | null |
Viewer ID extracted from cookie value. |
ViewerConnectedContainerProps¶
Ƭ ViewerConnectedContainerProps: Object
Type declaration¶
Name | Type |
---|---|
children |
ReactNode |
renderFallback? |
(connectionState : ViewerConnectionState <ModelTypes >) => null | Element |
ViewerConnectionState¶
Ƭ ViewerConnectionState<ModelTypes
>: { status
: "idle"
} | { promise
: Abortable
<SelfID
<ModelTypes
> | null
> ; provider
: EthereumAuthProvider
; status
: "connecting"
} | { selfID
: SelfID
<ModelTypes
> ; status
: "connected"
} | { error
: Error
; status
: "failed"
}
The viewer connection can be in one of the following states, identified by status
:
idle
: no connection has been attempted.connecting
: attempting to connect using the attachedprovider
. The attachedpromise
can be used to track the connection attempt.connected
: successfully connected with the attachedselfID
.failed
: connection attempted failed with the attachederror
.
Type parameters¶
Name | Type |
---|---|
ModelTypes |
extends ModelTypeAliases = CoreModelTypes |
ViewerRecord¶
Ƭ ViewerRecord<ContentType
>: { content?
: never
; error?
: never
; isError
: false
; isLoadable
: false
; isLoading
: false
; isMutable
: false
; isMutating
: false
; merge?
: never
; set?
: never
} | { content?
: ContentType
; error?
: unknown
; isError
: boolean
; isLoadable
: true
; isLoading
: boolean
; isMutable
: boolean
; isMutating
: boolean
; merge
: (content
: ContentType
) => Promise
<void
> ; set
: (content
: ContentType
) => Promise
<void
> }
A ViewerRecord provides an interface for interacting with record stored on Ceramic, depending on
the current ViewerID
value:
- If
null
, no interaction is possible with the record. - If it is an instance of
PublicID
, only reads are possible. - If it is an instance of
SelfID
, all interactions (reads and mutations) are possible.
The ViewerRecord object contains the following properties:
isLoadable
:false
if the viewer ID isnull
,true
otherwise.isLoading
:true
when the record is being loaded,false
otherwise.content
: the record contents, if loaded.isError
:true
when the record failed to load,false
otherwise.error
: possible error raised when attempting to load the record.isMutable
:true
if the viewer ID is an instance ofSelfID
,false
otherwise.isMutating
:true
when the record is being mutated as the result of calling the ViewerRecord objectmerge
orset
function,false
otherwise.set
: function used to replace the record contents using theset
method, only available ifisMutating
istrue
.merge
: function used to merge the record contents using themerge
method, only available ifisMutating
istrue
.
Type parameters¶
Name |
---|
ContentType |
Functions¶
Provider¶
▸ Provider<ModelTypes
>(props
): JSX.Element
Top-level provider component for using Self.ID's React APIs.
Type parameters¶
Name | Type |
---|---|
ModelTypes |
extends ModelTypeAliases <Record <string , any >, Record <string , string >, Record <string , string >> = ModelTypes |
Parameters¶
Name | Type |
---|---|
props |
ProviderProps <ModelTypes > |
Returns¶
JSX.Element
ViewerConnectedContainer¶
▸ ViewerConnectedContainer(props
): JSX.Element
| null
Container component for only rendering children
when the viewer is connected.
A renderFallback
function can be provided to render elements when the viewer is not connected.
The current ViewerConnectionState
is injected as function argument.
Parameters¶
Name | Type |
---|---|
props |
ViewerConnectedContainerProps |
Returns¶
JSX.Element
| null
formatDID¶
▸ formatDID(did
, maxLength?
): string
Parameters¶
Name | Type | Default value |
---|---|---|
did |
string |
undefined |
maxLength |
number |
20 |
Returns¶
string
getImageURL¶
▸ getImageURL(ipfsPrefix
, sources
, dimensions
): string
| undefined
Parameters¶
Name | Type |
---|---|
ipfsPrefix |
string |
sources |
undefined | ImageSources |
dimensions |
Dimensions |
Returns¶
string
| undefined
selectImageSource¶
▸ selectImageSource(sources
, dimensions
, mode?
): ImageMetadata
Select the best option from the given sources
to match the wanted dimensions
and mode
.
Parameters¶
Name | Type |
---|---|
sources |
ImageSources |
dimensions |
Dimensions |
mode? |
SizeMode |
Returns¶
ImageMetadata
uploadImage¶
▸ uploadImage(url
, file
, sizes?
): Promise
<ImageSources
>
Upload an image to IPFS, optionally with additional alternative sizes
.
Parameters¶
Name | Type |
---|---|
url |
string |
file |
File |
sizes? |
Dimensions [] |
Returns¶
Promise
<ImageSources
>
useClient¶
▸ useClient<ModelTypes
>(): ReactClient
<ModelTypes
>
Type parameters¶
Name | Type |
---|---|
ModelTypes |
extends ModelTypeAliases <Record <string , any >, Record <string , string >, Record <string , string >> = ModelTypes |
Returns¶
ReactClient
<ModelTypes
>
usePublicRecord¶
▸ usePublicRecord<ModelTypes
, Alias
, ContentType
>(alias
, id
): PublicRecord
<ContentType
| null
>
Hook for accessing the PublicRecord
for a given alias and account (DID or CAIP-10).
Type parameters¶
Name | Type |
---|---|
ModelTypes |
extends ModelTypeAliases <Record <string , any >, Record <string , string >, Record <string , string >> = ModelTypes |
Alias |
extends string | number | symbol = keyof ModelTypes ["definitions" ] |
ContentType |
DefinitionContentType <ModelTypes , Alias > |
Parameters¶
Name | Type |
---|---|
alias |
Alias |
id |
string |
Returns¶
PublicRecord
<ContentType
| null
>
useViewerConnection¶
▸ useViewerConnection<ModelTypes
>(): [ViewerConnectionState
<ModelTypes
>, (provider
: EthereumAuthProvider
) => Promise
<SelfID
<ModelTypes
> | null
>, () => void
]
Hook for handling the viewer's connection lifecycle, returning the following elements:
- The current
ViewerConnectionState
object. - A connection attempt function, taking an
EthereumAuthProvider
argument. - A reset function, clearing the current
ViewerID
.
Type parameters¶
Name | Type |
---|---|
ModelTypes |
extends ModelTypeAliases <Record <string , any >, Record <string , string >, Record <string , string >> = ModelTypes |
Returns¶
[ViewerConnectionState
<ModelTypes
>, (provider
: EthereumAuthProvider
) => Promise
<SelfID
<ModelTypes
> | null
>, () => void
]
useViewerID¶
▸ useViewerID<ModelTypes
>(): ViewerID
<ModelTypes
> | null
Type parameters¶
Name | Type |
---|---|
ModelTypes |
extends ModelTypeAliases <Record <string , any >, Record <string , string >, Record <string , string >> = ModelTypes |
Returns¶
ViewerID
<ModelTypes
> | null
useViewerRecord¶
▸ useViewerRecord<ModelTypes
, Alias
, ContentType
>(alias
): ViewerRecord
<ContentType
| null
>
Hook for accessing the ViewerRecord
for a given alias.
Type parameters¶
Name | Type |
---|---|
ModelTypes |
extends ModelTypeAliases <Record <string , any >, Record <string , string >, Record <string , string >> = ModelTypes |
Alias |
extends string | number | symbol = keyof ModelTypes ["definitions" ] |
ContentType |
DefinitionContentType <ModelTypes , Alias > |
Parameters¶
Name | Type |
---|---|
alias |
Alias |
Returns¶
ViewerRecord
<ContentType
| null
>