Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ React inside Phoenix LiveView.
- 💀 **Dead View** Support
- 🐌 **Lazy-loading** React Components
- 🦥 **Slot** Interoperability
- 🔗 **Link Component** for LiveView Navigation
- 🚀 **Amazing DX** with Vite

## Resources
Expand Down
4 changes: 4 additions & 0 deletions assets/copy/react-components/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { Simple } from "./simple";
import { LinkExample } from "./link-example";
import { Link } from "live_react";

export default {
Simple,
LinkExample,
Link,
};
40 changes: 40 additions & 0 deletions assets/copy/react-components/link-example.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from "react";
import { Link } from "live_react";

export function LinkExample({}) {
return (
<div className="space-y-4 p-4">
<h2 className="text-xl font-bold">Link Component Examples</h2>

<div className="space-y-2">
<p>
<Link href="/external" className="text-blue-600 hover:underline">
External Link (full page reload)
</Link>
</p>

<p>
<Link patch="/same-liveview" className="text-green-600 hover:underline">
Patch Link (same LiveView, calls handle_params)
</Link>
</p>

<p>
<Link navigate="/other-liveview" className="text-purple-600 hover:underline">
Navigate Link (different LiveView, same session)
</Link>
</p>

<p>
<Link
navigate="/replace-history"
replace={true}
className="text-red-600 hover:underline"
>
Replace History (navigate with replace)
</Link>
</p>
</div>
</div>
);
}
4 changes: 4 additions & 0 deletions assets/copy/react-components/link.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { Link } from "live_react";

// Re-export Link component for direct usage
export { Link };
15 changes: 15 additions & 0 deletions assets/js/live_react/index.d.mts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import React from "react";

export interface LiveProps {
pushEvent: (
event: string,
Expand All @@ -19,4 +21,17 @@ export interface LiveProps {
uploadTo: (target: string, name: string, files: FileList | File[]) => void;
}

export interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
/** Uses traditional browser navigation to the new location. This means the whole page is reloaded. */
href?: string | null;
/** Patches the current LiveView. The handle_params callback will be invoked with minimum content sent over the wire. */
patch?: string | null;
/** Navigates to a LiveView. Only works between LiveViews in the same live_session. */
navigate?: string | null;
/** When using patch or navigate, should the browser's history be replaced with pushState? */
replace?: boolean;
children?: React.ReactNode;
}

export function useLiveReact(): LiveProps;
export function Link(props: LinkProps): React.ReactElement;
1 change: 1 addition & 0 deletions assets/js/live_react/index.mjs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { getHooks } from "./hooks";
export { useLiveReact } from "./context";
export { Link } from "./link.jsx";
39 changes: 39 additions & 0 deletions assets/js/live_react/link.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useMemo } from "react";

/**
* Phoenix LiveView Link component for React
*
* Handles different types of navigation in Phoenix LiveView:
* - href: Traditional browser navigation (full page reload)
* - patch: Patches the current LiveView (calls handle_params)
* - navigate: Navigates to a different LiveView within the same live_session
* - replace: Whether to replace or push browser history
*/
export function Link({
href = null,
patch = null,
navigate = null,
replace = false,
children,
...attrs
}) {
const linkAttrs = useMemo(() => {
if (!patch && !navigate) {
return {
href: href || "#",
};
}

return {
href: (navigate ? navigate : patch) || "#",
"data-phx-link": navigate ? "redirect" : "patch",
"data-phx-link-state": replace ? "replace" : "push",
};
}, [href, patch, navigate, replace]);

return (
<a {...attrs} {...linkAttrs}>
{children}
</a>
);
}
2 changes: 2 additions & 0 deletions guides/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ The easiest way to get started with development is to clone live_react and run t
git clone https://github.com/mrdotb/live_react.git
cd live_react_examples
```

The examples include demonstrations of the Link component for LiveView navigation at `/link-demo` and `/link-usage`.
26 changes: 26 additions & 0 deletions guides/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,32 @@ children = [
<.react name="Simple" />
```

You can also use the built-in Link component for LiveView navigation:

```elixir
<!-- Use Link component directly in templates -->
<.react name="Link" href="/some-page">External Link</.react>
<.react name="Link" patch="/current-liveview?tab=new">Patch Link</.react>
<.react name="Link" navigate="/other-liveview">Navigate Link</.react>

<!-- Or import it in your React components -->
```

```javascript
import { Link } from "live_react";

function MyComponent() {
return (
<div>
<Link href="/external">Traditional Link</Link>
<Link patch="/same-lv?param=value">Patch Current LiveView</Link>
<Link navigate="/other-lv">Navigate to Other LiveView</Link>
<Link navigate="/replace" replace={true}>Replace History</Link>
</div>
);
}
```

14. (Optional) enable [stateful hot reload](https://twitter.com/jskalc/status/1788308446007132509) of phoenix LiveViews - it allows for stateful reload across the whole stack 🤯. Just adjust your `dev.exs` to look like this - add `notify` section and remove `live|components` from patterns.

```elixir
Expand Down
Loading