diff --git a/docs/core_docs/docs/expression_language/how_to/with_history.mdx b/docs/core_docs/docs/expression_language/how_to/with_history.mdx
new file mode 100644
index 000000000000..798c67e7587d
--- /dev/null
+++ b/docs/core_docs/docs/expression_language/how_to/with_history.mdx
@@ -0,0 +1,53 @@
+import CodeBlock from "@theme/CodeBlock";
+import Example from "@examples/guides/expression_language/runnable_history.ts";
+
+# Add message history (memory)
+
+The `RunnableWithMessageHistory` let's us add message history to certain types of chains.
+
+Specifically, it can be used for any Runnable that takes as input one of
+
+- a list of `BaseMessage`
+- an object with a key that takes a list of `BaseMessage`
+- an object with a key that takes the latest message(s) as a string or list of `BaseMessage`, and a separate key that takes historical messages
+
+And returns as output one of
+
+- a string that can be treated as the contents of an `AIMessage`
+- a list of `BaseMessage`
+- an object with a key that contains a list of `BaseMessage`
+
+Let's take a look at some examples to see how it works.
+
+<CodeBlock language="typescript">{Example}</CodeBlock>
+
+## Pass config through the constructor
+
+You don't always have to pass the `config` object through the `invoke` method. `RunnableWithMessageHistory` supports passing it through the constructor as well.
+
+To do this, the only change you need to make is remove the second arg (or just the `configurable` key from the second arg) from the `invoke` method, and add it in through the `config` key in the constructor.
+
+This is a simple example building on top of what we have above:
+
+```typescript
+const config: RunnableConfig = { configurable: { sessionId: "1" } };
+
+const withHistory = new RunnableWithMessageHistory({
+  runnable,
+  getMessageHistory: (_sessionId: string) => messageHistory,
+  inputMessagesKey: "input",
+  historyMessagesKey: "history",
+  // Passing config through here instead of through the invoke method
+  config,
+});
+
+let output = await withHistory.invoke({ input: "Hello there, I'm Archibald!" });
+console.log("output 1:", output);
+/**
+output 1: AIMessage {
+  lc_namespace: [ 'langchain_core', 'messages' ],
+  content: 'Hello, Archibald! How can I assist you today?',
+  additional_kwargs: { function_call: undefined, tool_calls: undefined }
+}
+ */
+```
diff --git a/examples/src/guides/expression_language/runnable_history.ts b/examples/src/guides/expression_language/runnable_history.ts
new file mode 100644
index 000000000000..afcd7aade6c2
--- /dev/null
+++ b/examples/src/guides/expression_language/runnable_history.ts
@@ -0,0 +1,69 @@
+import { ChatOpenAI } from "langchain/chat_models/openai";
+import { ChatMessageHistory } from "langchain/stores/message/in_memory";
+import { ChatPromptTemplate, MessagesPlaceholder } from "langchain/prompts";
+import {
+  RunnableConfig,
+  RunnableWithMessageHistory,
+} from "langchain/runnables";
+
+// Instantiate your model and prompt.
+const model = new ChatOpenAI({});
+const prompt = ChatPromptTemplate.fromMessages([
+  ["ai", "You are a helpful assistant"],
+  new MessagesPlaceholder("history"),
+  ["human", "{input}"],
+]);
+
+// Create a simple runnable which just chains the prompt to the model.
+const runnable = prompt.pipe(model);
+
+// Define your session history store.
+// This is where you will store your chat history.
+const messageHistory = new ChatMessageHistory();
+
+// Create your `RunnableWithMessageHistory` object, passing in the
+// runnable created above.
+const withHistory = new RunnableWithMessageHistory({
+  runnable,
+  // Optionally, you can use a function which tracks history by session ID.
+  getMessageHistory: (_sessionId: string) => messageHistory,
+  inputMessagesKey: "input",
+  // This shows the runnable where to insert the history.
+  // We set to "history" here because of our MessagesPlaceholder above.
+  historyMessagesKey: "history",
+});
+
+// Create your `configurable` object. This is where you pass in the
+// `sessionId` which is used to identify chat sessions in your message store.
+const config: RunnableConfig = { configurable: { sessionId: "1" } };
+
+// Pass in your question, in this example we set the input key
+// to be "input" so we need to pass an object with an "input" key.
+let output = await withHistory.invoke(
+  { input: "Hello there, I'm Archibald!" },
+  config
+);
+console.log("output 1:", output);
+/**
+output 1: AIMessage {
+  lc_namespace: [ 'langchain_core', 'messages' ],
+  content: 'Hello, Archibald! How can I assist you today?',
+  additional_kwargs: { function_call: undefined, tool_calls: undefined }
+}
+ */
+
+output = await withHistory.invoke({ input: "What's my name?" }, config);
+console.log("output 2:", output);
+/**
+output 2: AIMessage {
+  lc_namespace: [ 'langchain_core', 'messages' ],
+  content: 'Your name is Archibald, as you mentioned earlier. Is there anything specific you would like assistance with, Archibald?',
+  additional_kwargs: { function_call: undefined, tool_calls: undefined }
+}
+ */
+
+/**
+ * You can see the LangSmith traces here:
+ * output 1 @link https://smith.langchain.com/public/686f061e-bef4-4b0d-a4fa-04c107b6db98/r
+ * output 2 @link https://smith.langchain.com/public/c30ba77b-c2f4-440d-a54b-f368ced6467a/r
+ */
diff --git a/langchain-core/src/runnables/history.ts b/langchain-core/src/runnables/history.ts
index fbf12f89c551..29e65802eb79 100644
--- a/langchain-core/src/runnables/history.ts
+++ b/langchain-core/src/runnables/history.ts
@@ -22,7 +22,10 @@ import { RunnablePassthrough } from "./passthrough.js";
 type GetSessionHistoryCallable = (
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   ...args: Array<any>
-) => Promise<BaseChatMessageHistory | BaseListChatMessageHistory>;
+) =>
+  | Promise<BaseChatMessageHistory | BaseListChatMessageHistory>
+  | BaseChatMessageHistory
+  | BaseListChatMessageHistory;
 
 export interface RunnableWithMessageHistoryInputs<RunInput, RunOutput>
   extends Omit<