---
# System prepended metadata

title: Custom Serializers

---

# Custom Serializers

## Use cases

Third party CustomClass implements something important and requires methods to work with the data.

## Simplest possible usage


### PROBLEM
```typescript
export default component$(() => {
  const url = new URL(); // works!
  const value = new CustomClass(3434); // breaks serialization
  
  return (
    <div onClick$={() => {
      console.log(url);
      console.log(value.method());
    }} />
  )
});

```

### CURRENT SOLUTION 0 (userspace)

- :-1: state must be stored separate from CustomClass
- :-1: user must know shape of state to call methods
- :-1: state must be updated on every change
- :-1: CustomClass must be checked + inited in every callback

```typescript
import CustomClass from 'customClass'
// USER PROVIDES THIS
export const ensureClass = (theClass, obj) => {
  if (!obj.v)
    obj.v = noSerialize(new theClass(
      obj.state,
      // needs runtime serialization instead of once at pause time
      {updatePlainState: s=>obj.state=s}
    ))
  return obj
}
export default component$(() => {
  const url = new URL(); // works!
  // AWKWARD initialization
  const value = ensureClass(CustomClass, {
    state: 3434,
  })

  return (
    <div onClick$={
      async () => {
      const [url, value] = await useLexicalScope();

      // NEEDED IN ALL CALLBACKS
      ensureClass(CustomClass, value)

      console.log(url);
      console.log(value.v.method());
    }} />
  )
});
```


### SOLUTION 1 (changes useLexicalScope)

- :+1: state is exactly what is expected
- :+1: state is just the object
- :+1: state gets serialized only when needed
- :+1: CustomClass gets inited only on scope resume
- customSerializer$ tags object with parse+stringify
- when pausing, customSerializer-serializer stores QRL of both + stringified value
- when resuming, useLexicalScope imports parser and parses value
- useLexicalScope is async only on qwikloader calls
  - never from runtime calls
  - runtime call can preserialize everything
- stringifier is needed on pause so either pause is async or needs pre-import
- changes useLexicalScope
- optimizer needs to write call differently
- runtime needs to check scope for QRLs to load before calling handler

```typescript!
import CustomClass from 'customClass'

export default component$(() => {
  const url = new URL(); // works!
  // CustomClass defines .parse and .stringify
  const value = customSerializerQrl($(CustomClass), new CustomClass(3434));

  
  return (
    <div onClick$={
      () => useLexicalScope((url, value) => {
        console.log(url);
        console.log(value.method());
      })} />
  )
});
```

### SOLUTION 2 (pure runtime)

```typescript
import CustomClass from 'customClass'

export default component$(() => {
  const url = new URL(); // works!
  const value: CustomSerialze<CustomClass> = customSerializerQrl($(CustomClass), new CustomClass(3434));

  
  return (
    <div onClick$={
      async () => {
      const [url, value] = useLexicalScope();
      console.log(url);
      const a = await value.resolve(); // added friction
      console.log(a.method());
    }} />
  )
});
```

### SOLUTION 3 (async)

This breaks everything :)

```typescript
import CustomClass from 'customClass'

export default component$(() => {
  const url = new URL(); // works!
  const value: CustomSerialze<CustomClass> = customSerializerQrl($(CustomClass), new CustomClass(3434));

  
  return (
    <div onClick$={
      async () => {
      const [url, value] = await useLexicalScope();
      console.log(url);
      console.log(value.method());
    }} />
  )
});
```

## how qwikloader works when handler clicked
```json
// could await import all parsers
const mod = await import('path.js');
// could await import all parsers for this handler's scope
const handler = mod['symbol'];
globalContext = [element];
// could await parsers in handler
// BUT must read context first
handler();
globalContext = undefined;
```
  