This package will allow you to create shared components that will synchronize between server and client. This package is reflex-based, so it is important to know how to work with it to understand how this package works.
npm install @rbxts/shared-components-flameworkbash
npm install shared-components-flamework
`
š Getting Started
$3
`ts
// shared/components/counter.ts
// define state
interface State {
value: number;
}
@Component()
export class CounterComponent extends SharedComponent {
protected state = { value: 0 }; // Initial state
}
`
$3
`ts
// server/components/counter.ts
@Component({ tag: "Counter" })
export class ServerCounterComponent extends CounterComponent implements OnStart {
public onStart() {
task.spawn(() => {
while (task.wait(3)) {
this.Increment();
}
});
}
@Action() // State modifier
private Increment() {
return {
...this.state,
value: this.state.value + 1,
};
}
}
`
$3
`ts
// client/components/counter.ts
@Component({
tag: "Counter",
})
export class ClientCounterComponent extends CounterComponent {
@Subscribe((state) => state.value)
private onIncrement(newValue: number) {
print(new value: ${newValue});
}
}
`
š Networking
With this package you can declare remote event, action inside the component, this will allow you to easily make interaction between server and client component
`ts
@Component()
export class SomeSharedComponent extends SharedComponent<{}> {
protected state = {};
protected remotes = {
ClientEvent: SharedComponentNetwork.event(),
ServerEvent: SharedComponentNetwork.event(),
Action: SharedComponentNetwork.action<[value: number], void>(),
};
}
// server
@Component({
tag: "SomeSharedComponent",
})
export class ServerComponent extends SomeSharedComponent implements OnStart {
public onStart() {
this.remotes.ServerEvent.Connect((player, amount) => {
print( value = ${amount}, player: ${player});
});
this.remotes.Action.OnRequest((amount) => {
print(Action: value = ${amount});
});
task.wait(5);
this.remotes.ClientEvent.Broadcast(1);
}
}
// client
@Component({
tag: "SomeSharedComponent",
})
export class ClientComponent extends SomeSharedComponent implements OnStart {
public onStart() {
this.remotes.ClientEvent.Connect((amount: number) => {
print(value = ${amount});
});
this.remotes.ServerEvent.Fire(1);
this.remotes.Action(1);
}
}
`
š API Reference
$3
SharedComponent
The foundation class for all shared components
`ts
abstract class SharedComponent<
S = {}, // State type
A extends object = {}, // Attributes type
I extends Instance = Instance // Instance type
> extends BaseComponent
`
š·ļø Generics:
| Param | Description | Default |
|-------|-------------|---------|
| S | Component state type | any |
| A | Component attributes type | {} |
| I | Roblox Instance type | Instance |
š ļø Public Methods
GetState(): S
Returns current component state
`ts
const state = component.GetState();
print(Current value: ${state.value});
`
Dispatch(newState: S): S
Updates component state and triggers updates
`ts
component.Dispatch({
...component.GetState(),
value: 42
});
`
Subscribe()
State change subscription methods
Overload 1 - Full state
`ts
Subscribe(listener: (state: S, prevState: S) => void): () => void
`
Example:
`ts
const unsubscribe = component.Subscribe((state, prevState) => {
print(State changed from ${prevState.value} to ${state.value});
});
`
Overload 2 - Selected value
`ts
Subscribe(
selector: (state: S) => T,
listener: (value: T, prevValue: T) => void
): () => void
`
Example:
`ts
component.Subscribe(
state => state.counter,
(value, prevValue) => print(Counter changed: ${prevValue}ā${value})
);
`
GenerateInfo(): SharedComponentInfo
Generates component metadata
`ts
const info = component.GenerateInfo();
print(Component ID: ${info.ServerId});
`
ResolveIsSyncForPlayer(player: Player, data: SyncPatch): boolean
Controls state sync permissions
`ts
// Override to customize:
ResolveIsSyncForPlayer(player, patch) {
return true;
}
`
ResolveSyncForPlayer(player: Player, data: SyncPatch): SyncPatch
Filters/modifies state before syncing
`ts
ResolveSyncForPlayer(player, patch) {
return patch
}
`
ResolveConnectionPermission(player: Player): boolean
Determines whether a player is authorized to establish a connection to this shared component instance
`ts
ResolveConnectionPermission(player, patch) {
return patch
}
`
AttachDevTool(): void
Enables debugging interface
`ts
component.AttachDevTool(); // Sends the state in the REFLEX_DEVTOOLS event
`
DisableDevTool(): void
Disables debugging tools
`ts
component.DisableDevTool();
`
GetIsConnected(): boolean
š„ļø Client-only
Checks if component is connected to network
`ts
if (component.GetIsConnected()) {
print("Component is actively connected!");
}
`
Connect(): void
š„ļø Client-only
Establishes network connection to the server
`ts
component.Connect();
`
Disconnect(): void
š„ļø Client-only
Terminates network connection to the server
`ts
component.Disconnect();
`
IsConnectedPlayer(player: Player): boolean
š„ļø Server-only
Checks if specific player is connected
`ts
if (component.IsConnectedPlayer(player)) { ... }
`
GetConnectedPlayers(): ReadonlySet
š„ļø Server-only
Returns set of connected players
`ts
const players = component.GetConnectedPlayers();
`
DisconnectPlayer(player: Player): void
š„ļø Server-only
Force disconnects specific player
`ts
component.DisconnectPlayer(player);
`
OnConnected(): void
š„ļø Client-only
Fired when client component successfully connects
`ts
OnConnected() {
...
}
`
OnDisconnected(): void
š„ļø Client-only
Fired when client component disconnects
`ts
OnDisconnected() {
...
}
`
OnConnectedPlayer(player: Player): void
š„ļø Server-only
Fired when new player connects
`ts
OnConnectedPlayer(player: Player) {
...
}
`
OnDisconnectedPlayer(player: Player): void
š„ļø Server-only
Fired when player disconnects
`ts
OnDisconnectedPlayer(player: Player) {
...
}
`
š Protected Members
remotes: Record
Network communication interface
`ts
protected remotes = {
EventName: SharedComponentNetwork.event<[param1: type]>(),
ActionName: SharedComponentNetwork.action<[params], returnType>()
};
`
state: S
Initial state declaration
`ts
protected state = {
value: 0,
items: [] as string[]
};
`
isBlockingServerDispatches: boolean
š„ļø Client-only
When true, blocks all state updates from server
`ts
// Client code example
protected isBlockingServerDispatches = true; // Pause updates
// Later...
this.isBlockingServerDispatches = false; // Resume updates
`
isAutoConnect: boolean
š„ļø Client-only
When true (default), automatically connects on component initialization
`ts
// Disable auto-connect
protected isAutoConnect = false;
// Manual connection later
this.Connect();
`
šļø Decorators
@Action()
Marks state-modifying methods
`ts
@Action()
public increment(amount: number) {
return {
...this.state,
value: this.state.value + amount
};
}
`
@Subscribe()
Auto-subscribes methods to state changes
`ts
@Subscribe(state => state.value)
private onValueChange(value: number) {
// Called only when value changes
}
`
š Networking Utilities
SharedComponentNetwork
event()
Creates type-safe network event
`ts
const event = SharedComponentNetwork.event<[message: string, priority: number]>();
`
action()
Creates type-safe network action
`ts
const action = SharedComponentNetwork.action<[id: string], boolean>();
`
ā ļø Important Notes
Always return new state objects in actions or on dispatch:
`ts
// ā
Good
return {
...state,
value: newValue
};
// ā Bad
state.value = newValue;
return state;
``
shared-components-flamework is released under the MIT License.